diff --git a/bda/admin.py b/bda/admin.py index bcbc609c..8af71d00 100644 --- a/bda/admin.py +++ b/bda/admin.py @@ -5,7 +5,9 @@ from django.contrib.contenttypes.models import ContentType from django.contrib import admin from django.db.models import Sum, Count -from bda.models import Spectacle, Salle, Participant, ChoixSpectacle, Attribution +from bda.models import Spectacle, Salle, Participant, ChoixSpectacle, Attribution, Tirage + +from datetime import timedelta class ChoixSpectacleInline(admin.TabularInline): model = ChoixSpectacle @@ -37,62 +39,38 @@ class ParticipantAdmin(admin.ModelAdmin): actions_on_bottom = True list_per_page = 400 - def send_choices(self, request, queryset): - for member in queryset.all(): - choices = member.choixspectacle_set.order_by('priority').all() - if len(choices) == 0: - continue - mail = u"""Cher(e) %s, -Voici tes choix de spectacles tels que notre système les a enregistrés :\n\n""" % member.user.get_full_name() - next_rank = 1 - member_shows = {} - for choice in choices: - if choice.spectacle in member_shows: continue - else: member_shows[choice.spectacle] = True - extra = "" - if choice.double: - extra += u" ; deux places" - if choice.autoquit: - extra += u" ; désistement automatique" - mail += u"- Choix %d : %s%s\n" % (next_rank, choice.spectacle, extra) - next_rank += 1 - mail += u"""\nSi cette liste est incorrecte, merci de nous contacter au plus vite (avant samedi 6 octobre 18h). - -Artistiquement, -Le BdA""" - send_mail ("Choix de spectacles (BdA du COF)", mail, - "bda@ens.fr", [member.user.email], - fail_silently = True) - count = len(queryset.all()) - if count == 1: - message_bit = u"1 membre a" - plural = "" - else: - message_bit = u"%d membres ont" % count - plural = "s" - self.message_user(request, u"%s été informé%s avec succès." % (message_bit, plural)) - send_choices.short_description = u"Envoyer les choix par mail" - def send_attribs(self, request, queryset): for member in queryset.all(): attribs = member.attributions.all() if len(attribs) == 0: - continue - mail = u"""Cher(e) %s, + mail = u"""Cher-e %s, -Tu t'es inscrit(e) pour le tirage au sort du BdA. Tu as été sélectionné(e) +Tu t'es inscrit-e pour le tirage au sort du BdA. Malheureusement, tu n'as +obtenu aucune place. + +Nous proposons cependant de nombreuses offres hors-tirage tout au long de +l'année, et nous t'invitons à nous contacter si l'une d'entre elles t'intéresse ! +-- +Le Bureau des Arts + +""" + name = member.user.get_full_name() + mail = mail % name + else: + mail = u"""Cher-e %s, + +Tu t'es inscrit-e pour le tirage au sort du BdA. Tu as été sélectionné-e pour les spectacles suivants : %s *Paiement* -L'intégralité de ces places de spectacles est à régler à partir du lundi -6 octobre et AVANT le vendredi 10 octobre, au bureau du COF pendant les -heures de permanences (du lundi au vendredi entre 12h et 14h, et entre 18h -et 20h). Des facilités de paiement sont bien évidemment possibles : nous -pouvons ne pas encaisser le chèque immédiatement, ou bien découper votre -paiement en deux fois. Pour ceux qui ne pourraient pas venir payer au bureau, -merci de nous contacter par mail. +L'intégralité de ces places de spectacles est à régler dès maintenant et AVANT +le %s, au bureau du COF pendant les heures de permanences (du lundi au vendredi +entre 12h et 14h, et entre 18h et 20h). Des facilités de paiement sont bien +évidemment possibles : nous pouvons ne pas encaisser le chèque immédiatement, ou +bien découper votre paiement en deux fois. Pour ceux qui ne pourraient pas venir +payer au bureau, merci de nous contacter par mail. *Mode de retrait des places* Au moment du paiement, certaines places vous seront remises directement, d'autres @@ -109,16 +87,16 @@ prochainement disponible, directement sur votre compte GestioCOF. En vous souhaitant de très beaux spectacles tout au long de l'année, -- Le Bureau des Arts -(Jean, Antoine, Élodie, Marion et Louise) """ - attribs_text = "" - name = member.user.get_full_name() - for attrib in attribs: - attribs_text += u"- 1 place pour %s\n" % attrib - mail = mail % (name, attribs_text) - send_mail ("Résultats du tirage au sort", mail, - "bda@ens.fr", [member.user.email], - fail_silently = True) + attribs_text = "" + name = member.user.get_full_name() + for attrib in attribs: + attribs_text += u"- 1 place pour %s\n" % attrib + deadline = member.tirage.fermeture + timedelta(days=7) + mail = mail % (name, attribs_text, deadline.strftime('%d %b %Y')) + send_mail ("Résultats du tirage au sort", mail, + "bda@ens.fr", [member.user.email], + fail_silently = True) count = len(queryset.all()) if count == 1: message_bit = u"1 membre a" @@ -155,3 +133,4 @@ admin.site.register(Salle) admin.site.register(Participant, ParticipantAdmin) admin.site.register(Attribution, AttributionAdmin) admin.site.register(ChoixSpectacle, ChoixSpectacleAdmin) +admin.site.register(Tirage) diff --git a/bda/algorithm.py b/bda/algorithm.py index 623f9756..bce41282 100644 --- a/bda/algorithm.py +++ b/bda/algorithm.py @@ -59,12 +59,6 @@ class Algorithm(object): self.origranks[member][show], self.choices[member][show].double)) - """ - Pour les 2 Walkyries: c'est Sandefer - - Pour les 4 places pour l'Oratorio c'est Maxence Arutkin - """ - def __call__(self, seed): random.seed(seed) results = [] @@ -102,3 +96,4 @@ class Algorithm(object): self.IncrementRanks(member, i) results.append((show,winners,losers)) return results + diff --git a/bda/forms.py b/bda/forms.py index 8a652296..d61623db 100644 --- a/bda/forms.py +++ b/bda/forms.py @@ -1,7 +1,7 @@ # coding: utf-8 from django import forms -from django.forms.models import inlineformset_factory, BaseInlineFormSet +from django.forms.models import BaseInlineFormSet from bda.models import Spectacle, Participant, ChoixSpectacle, Attribution class BaseBdaFormSet(BaseInlineFormSet): @@ -19,19 +19,21 @@ class BaseBdaFormSet(BaseInlineFormSet): spectacle = form.cleaned_data['spectacle'] delete = form.cleaned_data['DELETE'] if not delete and spectacle in spectacles: - raise forms.ValidationError("Vous ne pouvez pas vous inscrire deux fois pour le même spectacle.") + raise forms.ValidationError("Vous ne pouvez pas vous " + \ + "inscrire deux fois pour le même spectacle.") spectacles.append(spectacle) class TokenForm(forms.Form): - token = forms.CharField(widget = forms.widgets.Textarea()) + token = forms.CharField(widget=forms.widgets.Textarea()) class SpectacleModelChoiceField(forms.ModelChoiceField): def label_from_instance(self, obj): - return u"%s le %s (%s) à %.02f€" % (obj.title, obj.date_no_seconds(), obj.location, obj.price) + return u"%s le %s (%s) à %.02f€" % (obj.title, obj.date_no_seconds(), + obj.location, obj.price) class ResellForm(forms.Form): count = forms.ChoiceField(choices = (("1","1"),("2","2"),)) - spectacle = SpectacleModelChoiceField(queryset = Spectacle.objects.none()) + spectacle = SpectacleModelChoiceField(queryset=Spectacle.objects.none()) def __init__(self, participant, *args, **kwargs): super(ResellForm, self).__init__(*args, **kwargs) diff --git a/bda/migrations/0002_add_tirage.py b/bda/migrations/0002_add_tirage.py new file mode 100644 index 00000000..1956a4a4 --- /dev/null +++ b/bda/migrations/0002_add_tirage.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +from django.conf import settings +from django.utils import timezone + +def forwards_func(apps, schema_editor): + Tirage = apps.get_model("bda", "Tirage") + db_alias = schema_editor.connection.alias + Tirage.objects.using(db_alias).bulk_create([ + Tirage( + id=1, + title="Tirage de test (migration)", + active=False, + ouverture=timezone.now(), + fermeture=timezone.now()), + ]) + +class Migration(migrations.Migration): + + dependencies = [ + ('bda', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Tirage', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('title', models.CharField(max_length=300, verbose_name=b'Titre')), + ('ouverture', models.DateTimeField(verbose_name=b"Date et heure d'ouverture du tirage")), + ('fermeture', models.DateTimeField(verbose_name=b'Date et heure de fermerture du tirage')), + ('token', models.TextField(verbose_name=b'Graine du tirage', blank=True)), + ('active', models.BooleanField(default=True, verbose_name=b'Tirage actif')), + ], + ), + migrations.RunPython(forwards_func, migrations.RunPython.noop), + migrations.AlterField( + model_name='participant', + name='user', + field=models.ForeignKey(to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='participant', + name='tirage', + field=models.ForeignKey(default=1, to='bda.Tirage'), + preserve_default=False, + ), + migrations.AddField( + model_name='spectacle', + name='tirage', + field=models.ForeignKey(default=1, to='bda.Tirage'), + preserve_default=False, + ), + ] diff --git a/bda/models.py b/bda/models.py index 11c9dd3b..cab8ce41 100644 --- a/bda/models.py +++ b/bda/models.py @@ -7,22 +7,36 @@ from django.contrib.auth.models import User from django.utils.translation import ugettext_lazy as _ from django.db.models.signals import post_save -class Salle (models.Model): - name = models.CharField ("Nom", max_length = 300) - address = models.TextField ("Adresse") +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") + token = models.TextField("Graine du tirage", blank=True) + active = models.BooleanField("Tirage actif", default=False) + + def date_no_seconds(self): + return self.fermeture.strftime('%d %b %Y %H:%M') + + def __unicode__(self): + return u"%s - %s" % (self.title, self.date_no_seconds()) + +class Salle(models.Model): + name = models.CharField("Nom", max_length = 300) + address = models.TextField("Adresse") def __unicode__ (self): return self.name -class Spectacle (models.Model): - title = models.CharField ("Titre", max_length = 300) - date = models.DateTimeField ("Date & heure") +class Spectacle(models.Model): + title = models.CharField("Titre", max_length=300) + date = models.DateTimeField("Date & heure") location = models.ForeignKey(Salle) - description = models.TextField ("Description", blank = True) - slots_description = models.TextField ("Description des places", blank = True) - price = models.FloatField("Prix d'une place", blank = True) - slots = models.IntegerField ("Places") - priority = models.IntegerField ("Priorité", default = 1000) + description = models.TextField("Description", blank=True) + slots_description = models.TextField("Description des places", blank=True) + price = models.FloatField("Prix d'une place") + slots = models.IntegerField("Places") + priority = models.IntegerField("Priorité", default=1000) + tirage = models.ForeignKey(Tirage) class Meta: verbose_name = "Spectacle" @@ -38,7 +52,8 @@ class Spectacle (models.Model): return self.date.strftime('%d %b %Y %H:%M') def __unicode__ (self): - return u"%s - %s, %s, %.02f€" % (self.title, self.date_no_seconds(), self.location, self.price) + return u"%s - %s, %s, %.02f€" % (self.title, self.date_no_seconds(), + self.location, self.price) PAYMENT_TYPES = ( ("cash",u"Cash"), @@ -47,12 +62,18 @@ PAYMENT_TYPES = ( ("autre",u"Autre"), ) -class Participant (models.Model): - user = models.OneToOneField(User) - choices = models.ManyToManyField(Spectacle, through = "ChoixSpectacle", related_name = "chosen_by") - attributions = models.ManyToManyField(Spectacle, through = "Attribution", related_name = "attributed_to") - paid = models.BooleanField (u"A payé", default = False) - paymenttype = models.CharField(u"Moyen de paiement", max_length = 6, choices = PAYMENT_TYPES, blank = True) +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 (u"A payé", default=False) + paymenttype = models.CharField(u"Moyen de paiement", + max_length=6, choices=PAYMENT_TYPES, blank=True) + tirage = models.ForeignKey(Tirage) def __unicode__ (self): return u"%s" % (self.user) @@ -63,11 +84,12 @@ DOUBLE_CHOICES = ( ("double", "2 places sinon rien"), ) -class ChoixSpectacle (models.Model): +class ChoixSpectacle(models.Model): participant = models.ForeignKey(Participant) - spectacle = models.ForeignKey(Spectacle, related_name = "participants") + 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) + double_choice = models.CharField("Nombre de places", + default="1", choices=DOUBLE_CHOICES, max_length=10) def get_double(self): return self.double_choice != "1" @@ -83,10 +105,11 @@ class ChoixSpectacle (models.Model): verbose_name = "voeu" verbose_name_plural = "voeux" -class Attribution (models.Model): +class Attribution(models.Model): participant = models.ForeignKey(Participant) - spectacle = models.ForeignKey(Spectacle, related_name = "attribues") - given = models.BooleanField(u"Donnée", default = False) + spectacle = models.ForeignKey(Spectacle, related_name="attribues") + given = models.BooleanField(u"Donnée", default=False) def __unicode__ (self): return u"%s -- %s" % (self.participant, self.spectacle) + diff --git a/bda/templates/bda-attrib-extra.html b/bda/templates/bda-attrib-extra.html index c2c266d2..8ffccf34 100644 --- a/bda/templates/bda-attrib-extra.html +++ b/bda/templates/bda-attrib-extra.html @@ -3,7 +3,8 @@ {% block extracontent %}

Attribution (détails)

-

Token : {{ token }}

+

Token :

+
{{ token }}

Placés : {{ total_slots }} ; Déçus : {{ total_losers }}

diff --git a/bda/templates/bda-attrib.html b/bda/templates/bda-attrib.html index 22f5786a..f0bfd955 100644 --- a/bda/templates/bda-attrib.html +++ b/bda/templates/bda-attrib.html @@ -8,7 +8,8 @@ {% block realcontent %}

Attribution

-

Token : {{ token }}

+

Token :

+
{{ token }}

Placés : {{ total_slots }} ; Déçus : {{ total_losers }}

{% if user.profile.is_buro %}

Déficit total: {{ total_deficit }} €, Opéra: {{ opera_deficit }} €, Attribué: {{ total_sold }} €

{% endif %}

Temps de calcul : {{ duration|floatformat }}s

diff --git a/bda/templates/inscription-bda.html b/bda/templates/inscription-bda.html index 8ee08dc9..5f529eef 100644 --- a/bda/templates/inscription-bda.html +++ b/bda/templates/inscription-bda.html @@ -98,7 +98,7 @@ var django = { {% if stateerror %}

Impossible d'enregistrer vos modifications: vous avez apporté d'autres modifications entre temps

{% endif %} - + {% csrf_token %} {% include "inscription-formset.html" %} diff --git a/bda/templates/inscription-bda.html.save b/bda/templates/inscription-bda.html.save deleted file mode 100644 index c387943f..00000000 --- a/bda/templates/inscription-bda.html.save +++ /dev/null @@ -1,116 +0,0 @@ -{% extends "base_title.html" %} - -{% block extra_head %} - - - - - -{% endblock %} - -{% block realcontent %} - - -

Inscription au tirage au sort du BDA

- {% if success %} -

Votre inscription a été mise à jour avec succès !

- {% endif %} - - {% csrf_token %} - {% include "inscription-formset.html" %} - - - - Prix total actuel : {{ total_price }}€ -
-

- 1: demander deux places pour ce spectacle
- 2: abandonner une place si impossible d'en obtenir une seconde pour ce spectacle (si vous avez coché l'option Deux places pour ce spectacle)
- 3: cette liste de vœu est ordonnée (du plus important au moins important), pour ajuster la priorité vous pouvez déplacer chaque vœu
-

- -{% endblock %} diff --git a/bda/templates/inscription-bda2.html b/bda/templates/inscription-bda2.html deleted file mode 100644 index 91b08b93..00000000 --- a/bda/templates/inscription-bda2.html +++ /dev/null @@ -1,120 +0,0 @@ -{% extends "base_title.html" %} - -{% block extra_head %} - - - - - -{% endblock %} - -{% block realcontent %} - - -

Inscription au tirage au sort du BdA

- {% if success %} -

Votre inscription a été mise à jour avec succès !

- {% endif %} - {% if stateerror %} -

Impossible d'enregistrer vos modifications: vous avez apporté d'autres modifications entre temps

- {% endif %} - - {% csrf_token %} - {% include "inscription-formset.html" %} - - - - - Prix total actuel : {{ total_price }}€ -
-

- 1: demander deux places pour ce spectable
- 2: abandonner une place si impossible d'en obtenir une seconde pour ce spectacle (si vous avez coché l'option Deux places pour ce spectacle)
- 3: cette liste de vœu est ordonnée (du plus important au moins important), pour ajuster la priorité vous pouvez déplacer chaque vœu
-

- -{% endblock %} diff --git a/bda/templates/inscription-bda3.html b/bda/templates/inscription-bda3.html deleted file mode 100644 index bf77bac8..00000000 --- a/bda/templates/inscription-bda3.html +++ /dev/null @@ -1,120 +0,0 @@ -{% extends "base_title.html" %} - -{% block extra_head %} - - - - - -{% endblock %} - -{% block realcontent %} - - -

Inscription au tirage au sort du BdA

- {% if success %} -

Votre inscription a été mise à jour avec succès !

- {% endif %} - {% if stateerror %} -

Impossible d'enregistrer vos modifications: vous avez apporté d'autres modifications entre temps

- {% endif %} - - {% csrf_token %} - {% include "inscription-formset.html" %} - - - - - Prix total actuel : {{ total_price }}€ -
-

- 1: demander deux places pour ce spectable
- 2: abandonner une place si impossible d'en obtenir une seconde pour ce spectacle (si vous avez coché l'option Deux places pour ce spectacle)
- 3: cette liste de vœu est ordonnée (du plus important au moins important), pour ajuster la priorité vous pouvez déplacer chaque vœu
-

- -{% endblock %} diff --git a/bda/templates/resume_places.html b/bda/templates/resume_places.html index 4d9a8294..8954d7c3 100644 --- a/bda/templates/resume_places.html +++ b/bda/templates/resume_places.html @@ -12,7 +12,7 @@ {% endfor %}

Total à payer : {{ total|floatformat }}€

-

Exporter au format calendrier (.ics, compatible avec tous les logiciels d'agenda)

+

Exporter au format calendrier (.ics, compatible avec tous les logiciels d'agenda)

{% else %}

Vous n'avez aucune place :(

{% endif %} diff --git a/bda/templates/spectacle_list.html b/bda/templates/spectacle_list.html index 8c44b14e..d707dde4 100644 --- a/bda/templates/spectacle_list.html +++ b/bda/templates/spectacle_list.html @@ -3,9 +3,9 @@ {% block realcontent %}

Spectacles

{% endblock %} diff --git a/bda/views.py b/bda/views.py index f46f468a..ed653b54 100644 --- a/bda/views.py +++ b/bda/views.py @@ -6,25 +6,39 @@ from django.contrib.auth.decorators import login_required from django.db import models from django.http import Http404 from django.core import serializers +from django.forms.models import inlineformset_factory import hashlib from django.core.mail import send_mail +from django.utils import timezone +from django.views.generic.list import ListView -from datetime import datetime, timedelta +from datetime import timedelta 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 +from bda.models import Spectacle, Participant, ChoixSpectacle, Attribution, Tirage from bda.algorithm import Algorithm from bda.forms import BaseBdaFormSet, TokenForm, ResellForm @cof_required -def etat_places(request): - spectacles1 = ChoixSpectacle.objects.filter(double_choice = "1").all().values('spectacle','spectacle__title').annotate(total = models.Count('spectacle')) - spectacles2 = ChoixSpectacle.objects.exclude(double_choice = "1").all().values('spectacle','spectacle__title').annotate(total = models.Count('spectacle')) - spectacles = Spectacle.objects.all() +def etat_places(request, tirage_id): + tirage = get_object_or_404(Tirage, id=tirage_id) + spectacles1 = ChoixSpectacle.objects \ + .filter(spectacle__tirage=tirage) \ + .filter(double_choice="1") \ + .all() \ + .values('spectacle','spectacle__title') \ + .annotate(total=models.Count('spectacle')) + spectacles2 = ChoixSpectacle.objects \ + .filter(spectacle__tirage=tirage) \ + .exclude(double_choice="1") \ + .all() \ + .values('spectacle','spectacle__title') \ + .annotate(total=models.Count('spectacle')) + spectacles = tirage.spectacle_set.all() spectacles_dict = {} total = 0 for spectacle in spectacles: @@ -33,13 +47,18 @@ def etat_places(request): spectacles_dict[spectacle.id] = spectacle for spectacle in spectacles1: spectacles_dict[spectacle["spectacle"]].total += spectacle["total"] - spectacles_dict[spectacle["spectacle"]].ratio = spectacles_dict[spectacle["spectacle"]].total/float(spectacles_dict[spectacle["spectacle"]].slots) + spectacles_dict[spectacle["spectacle"]].ratio = \ + spectacles_dict[spectacle["spectacle"]].total / \ + float(spectacles_dict[spectacle["spectacle"]].slots) total += spectacle["total"] for spectacle in spectacles2: spectacles_dict[spectacle["spectacle"]].total += 2*spectacle["total"] - spectacles_dict[spectacle["spectacle"]].ratio = spectacles_dict[spectacle["spectacle"]].total/float(spectacles_dict[spectacle["spectacle"]].slots) + spectacles_dict[spectacle["spectacle"]].ratio = \ + spectacles_dict[spectacle["spectacle"]].total / \ + float(spectacles_dict[spectacle["spectacle"]].slots) total += spectacle["total"] - return render(request, "etat-places.html", {"spectacles": spectacles, "total": total}) + return render(request, "etat-places.html", + {"spectacles": spectacles, "total": total, 'tirage': tirage}) def _hash_queryset(queryset): data = serializers.serialize("json", queryset) @@ -48,9 +67,12 @@ def _hash_queryset(queryset): return hasher.hexdigest() @cof_required -def places(request): - participant, created = Participant.objects.get_or_create(user = request.user) - places = participant.attribution_set.order_by("spectacle__date", "spectacle").all() +def places(request, tirage_id): + tirage = get_object_or_404(Tirage, id=tirage_id) + participant, created = Participant.objects.get_or_create( + user=request.user, tirage=tirage) + places = participant.attribution_set.order_by( + "spectacle__date", "spectacle").all() total = sum([place.spectacle.price for place in places]) filtered_places = [] places_dict = {} @@ -73,13 +95,17 @@ def places(request): return render(request, "resume_places.html", {"participant": participant, "places": filtered_places, + "tirage": tirage, "total": total, "warning": warning}) @cof_required -def places_ics(request): - participant, created = Participant.objects.get_or_create(user = request.user) - places = participant.attribution_set.order_by("spectacle__date", "spectacle").all() +def places_ics(request, tirage_id): + tirage = get_object_or_404(Tirage, id=tirage_id) + participant, created = Participant.objects.get_or_create( + user=request.user, tirage=tirage) + places = participant.attribution_set.order_by( + "spectacle__date", "spectacle").all() filtered_places = [] places_dict = {} spectacles = [] @@ -98,45 +124,75 @@ def places_ics(request): "places": filtered_places}, content_type="text/calendar") @cof_required -def inscription(request): - if datetime.now() > datetime(2015, 10, 4, 12, 00): - participant, created = Participant.objects.get_or_create(user = request.user) +def inscription(request, tirage_id): + tirage = get_object_or_404(Tirage, id=tirage_id) + if timezone.now() < tirage.ouverture: + error_desc = "Ouverture le %s" % ( + tirage.ouverture.strftime('%d %b %Y à %H:%M')) + return render(request, 'resume_inscription.html', + { "error_title": "Le tirage n'est pas encore ouvert !", + "error_description": error_desc }) + if timezone.now() > tirage.fermeture: + participant, created = Participant.objects.get_or_create( + user=request.user, tirage=tirage) choices = participant.choixspectacle_set.order_by("priority").all() - return render(request, "resume_inscription.html", {"error_title": "C'est fini !", "error_description": u"Tirage au sort dans la journée !", "choices": choices}) - BdaFormSet = inlineformset_factory(Participant, ChoixSpectacle, fields = ("spectacle","double_choice","priority",), formset = BaseBdaFormSet) - participant, created = Participant.objects.get_or_create(user = request.user) + return render(request, "resume_inscription.html", + { "error_title": "C'est fini !", + "error_description": u"Tirage au sort dans la journée !", + "choices": choices}) + def formfield_callback(f, **kwargs): + if f.name == "spectacle": + kwargs['queryset'] = tirage.spectacle_set + return f.formfield(**kwargs) + BdaFormSet = inlineformset_factory( + Participant, + ChoixSpectacle, + fields=("spectacle","double_choice","priority"), + formset=BaseBdaFormSet, + formfield_callback=formfield_callback) + participant, created = Participant.objects.get_or_create( + user=request.user, tirage=tirage) success = False stateerror = False if request.method == "POST": dbstate = _hash_queryset(participant.choixspectacle_set.all()) if "dbstate" in request.POST and dbstate != request.POST["dbstate"]: stateerror = True - formset = BdaFormSet(instance = participant) + formset = BdaFormSet(instance=participant) else: - formset = BdaFormSet(request.POST, instance = participant) + formset = BdaFormSet(request.POST, instance=participant) if formset.is_valid(): - #ChoixSpectacle.objects.filter(participant = participant).delete() formset.save() success = True - formset = BdaFormSet(instance = participant) + formset = BdaFormSet(instance=participant) else: - formset = BdaFormSet(instance = participant) + formset = BdaFormSet(instance=participant) dbstate = _hash_queryset(participant.choixspectacle_set.all()) total_price = 0 for choice in participant.choixspectacle_set.all(): total_price += choice.spectacle.price if choice.double: total_price += choice.spectacle.price - return render(request, "inscription-bda.html", {"formset": formset, "success": success, "total_price": total_price, "dbstate": dbstate, "stateerror": stateerror}) + return render(request, "inscription-bda.html", + { "formset": formset, + "success": success, + "total_price": total_price, + "dbstate": dbstate, + 'tirage': tirage, + "stateerror": stateerror}) -def do_tirage(request): +def do_tirage(request, tirage_id): + tirage_elt = get_object_or_404(Tirage, id=tirage_id) form = TokenForm(request.POST) if not form.is_valid(): return tirage(request) + tirage_elt.token = form.cleaned_data['token'] + tirage_elt.save() start = time.time() data = {} - shows = Spectacle.objects.select_related().all() - members = Participant.objects.all() - choices = ChoixSpectacle.objects.order_by('participant', 'priority').select_related().all() + shows = tirage_elt.spectacle_set.select_related().all() + members = tirage_elt.spectacle_set.all() + choices = ChoixSpectacle.objects.filter(spectacle__tirage=tirage_elt).order_by( + 'participant', 'priority').select_related().all() algo = Algorithm(shows, members, choices) results = algo(form.cleaned_data["token"]) total_slots = 0 @@ -178,23 +234,26 @@ def do_tirage(request): members2[member].append(show) member.total += show.price members2 = members2.items() - data["members2"] = sorted(members2, key = lambda m: m[0].user.last_name) - if False and request.user.username in ["seguin", "harazi","fromherz", "ccadiou"]: + data["members2"] = sorted(members2, key=lambda m: m[0].user.last_name) + # À partir d'ici, le tirage devient effectif + # FIXME: Établir les conditions de validations (formulaire ?) + # cf. issue #32 + if False: Attribution.objects.all().delete() for (show, members, _) in results: for (member, _, _, _) in members: - attrib = Attribution(spectacle = show, participant = member) + attrib = Attribution(spectacle=show, participant=member) attrib.save() return render(request, "bda-attrib-extra.html", data) else: return render(request, "bda-attrib.html", data) -@login_required -def tirage(request): +@buro_required +def tirage(request, tirage_id): if request.POST: form = TokenForm(request.POST) if form.is_valid(): - return do_tirage(request) + return do_tirage(request, tirage_id) else: form = TokenForm() return render(request, "bda-token.html", {"form": form}) @@ -220,15 +279,19 @@ def do_resell(request, form): Je souhaite revendre %s pour %s le %s (%s) à %.02f€. Contactez moi par email si vous êtes intéressé·e·s ! -%s (%s)""" % (places, spectacle.title, spectacle.date_no_seconds(), spectacle.location, spectacle.price, request.user.get_full_name(), request.user.email) +%s (%s)""" % (places, spectacle.title, spectacle.date_no_seconds(), + spectacle.location, spectacle.price, request.user.get_full_name(), + request.user.email) send_mail("%s" % spectacle, mail, request.user.email, ["bda-revente@lists.ens.fr"], fail_silently = False) return render(request, "bda-success.html", {"show": spectacle, "places": places}) @login_required -def revente(request): - participant, created = Participant.objects.get_or_create(user = request.user) +def revente(request, tirage_id): + tirage = get_object_or_404(Tirage, id=tirage_id) + participant, created = Participant.objects.get_or_create( + user=request.user, tirage=tirage) if not participant.paid: return render(request, "bda-notpaid.html", {}) if request.POST: @@ -237,21 +300,40 @@ def revente(request): return do_resell(request, form) else: form = ResellForm(participant) - return render(request, "bda-revente.html", {"form": form}) + return render(request, "bda-revente.html", {"form": form, 'tirage': tirage}) @buro_required -def spectacle(request, spectacle_id): - spectacle = get_object_or_404(Spectacle, id = spectacle_id) +def spectacle(request, tirage_id, spectacle_id): + tirage = get_object_or_404(Tirage, id=tirage_id) + spectacle = get_object_or_404(Spectacle, id = spectacle_id, tirage=tirage) return render(request, "bda-emails.html", {"spectacle": spectacle}) + +class SpectacleListView(ListView): + model = Spectacle + template_name = 'spectacle_list.html' + def get_queryset(self): + self.tirage = get_object_or_404(Tirage, id=self.kwargs['tirage_id']) + categories = self.tirage.spectacle_set + return categories + def get_context_data(self, **kwargs): + context = super(SpectacleListView, self).get_context_data(**kwargs) + context['tirage_id'] = self.tirage.id + return context + @buro_required -def unpaid(request): - return render(request, "bda-unpaid.html", {"unpaid": Participant.objects.filter(paid = False).all()}) +def unpaid(request, tirage_id): + tirage = get_object_or_404(Tirage, id=tirage_id) + unpaid = tirage.participant_set.filter(paid=False).all() + return render(request, "bda-unpaid.html", {"unpaid": unpaid}) @buro_required -def liste_spectacles_ics(request): - spectacles = Spectacle.objects.order_by("date").all() +def liste_spectacles_ics(request, tirage_id): + tirage = get_object_or_404(Tirage, id=tirage_id) + spectacles = tirage.spectacle_set.order_by("date").all() for spectacle in spectacles: spectacle.dtend = spectacle.date + timedelta(seconds=7200) return render(request, "liste_spectacles.ics", - {"spectacles": spectacles}, content_type="text/calendar") + {"spectacles": spectacles, "tirage": tirage}, + content_type="text/calendar") + diff --git a/bda2/__init__.py b/bda2/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/bda2/admin.py b/bda2/admin.py deleted file mode 100644 index 84eebfa3..00000000 --- a/bda2/admin.py +++ /dev/null @@ -1,177 +0,0 @@ -# coding: utf-8 - -from django.core.mail import send_mail -from django.contrib.contenttypes.models import ContentType - -from django.contrib import admin -from django.db.models import Sum, Count -from bda2.models import Spectacle, Salle, Participant, ChoixSpectacle, Attribution - -class ChoixSpectacleInline(admin.TabularInline): - model = ChoixSpectacle - sortable_field_name = "priority" - -class AttributionInline(admin.TabularInline): - model = Attribution - -class ParticipantAdmin(admin.ModelAdmin): - #inlines = [ChoixSpectacleInline] - inlines = [AttributionInline] - def get_queryset(self, request): - return Participant.objects.annotate(nb_places = Count('attributions'), - total = Sum('attributions__price')) - def nb_places(self, obj): - return obj.nb_places - nb_places.admin_order_field = "nb_places" - nb_places.short_description = "Nombre de places" - def total(self, obj): - tot = obj.total - if tot: return u"%.02f €" % tot - else: return u"0 €" - total.admin_order_field = "total" - total.short_description = "Total à payer" - list_display = ("user", "nb_places", "total", "paid", "paymenttype") - list_filter = ("paid",) - search_fields = ('user__username', 'user__first_name', 'user__last_name') - actions = ['send_attribs',] - actions_on_bottom = True - list_per_page = 400 - - def send_choices(self, request, queryset): - for member in queryset.all(): - choices = member.choixspectacle_set.order_by('priority').all() - if len(choices) == 0: - continue - mail = u"""Cher(e) %s, -Voici tes choix de spectacles tels que notre système les a enregistrés :\n\n""" % member.user.get_full_name() - next_rank = 1 - member_shows = {} - for choice in choices: - if choice.spectacle in member_shows: continue - else: member_shows[choice.spectacle] = True - extra = "" - if choice.double: - extra += u" ; deux places" - if choice.autoquit: - extra += u" ; désistement automatique" - mail += u"- Choix %d : %s%s\n" % (next_rank, choice.spectacle, extra) - next_rank += 1 - mail += u"""\nSi cette liste est incorrecte, merci de nous contacter au plus vite (avant samedi 6 octobre 18h). - -Artistiquement, -Le BdA""" - send_mail ("Choix de spectacles (BdA du COF)", mail, - "bda@ens.fr", [member.user.email], - fail_silently = True) - count = len(queryset.all()) - if count == 1: - message_bit = u"1 membre a" - plural = "" - else: - message_bit = u"%d membres ont" % count - plural = "s" - self.message_user(request, u"%s été informé%s avec succès." % (message_bit, plural)) - send_choices.short_description = u"Envoyer les choix par mail" - - def send_attribs(self, request, queryset): - for member in queryset.all(): - attribs = member.attributions.all() - if len(attribs) == 0: - mail = u"""Cher-e %s, - -Tu t'es inscrit-e pour le tirage au sort du BdA. Malheureusement, tu n'as -obtenu aucune place. - -Nous sommes conscients que le nombre de places proposées lors de ce tirage -au sort est très restreint, mais nous sommes limités par les quotas que -nous imposent les théâtres. - -Nous proposons cependant de nombreuses offres hors-tirage tout au long de -l'année, et nous t'invitons à nous contacter si l'une d'entre elles t'intéresse ! --- -Le (nouveau) Bureau des Arts -(Thomas, Caroline, Antonin, Cécile, Hugo) - -""" - name = member.user.get_full_name() - mail = mail % name - else: - mail = u"""Cher-e %s, - -Tu t'es inscrit-e pour le tirage au sort du BdA. Tu as été sélectionné-e -pour les spectacles suivants : - -%s - -Nous sommes conscients que le nombre de places proposées lors de ce tirage -au sort est très restreint, mais nous sommes limités par les quotas que -nous imposent les théâtres. - -*Paiement* -L'intégralité de ces places de spectacles est à régler à partir du lundi -18 janvier et AVANT le vendredi 22 janvier, au bureau du COF pendant les -heures de permanences (du lundi au vendredi entre 12h et 14h, et entre 18h -et 20h). Des facilités de paiement sont bien évidemment possibles : nous -pouvons ne pas encaisser le chèque immédiatement, ou bien découper votre -paiement en deux fois. Il est possible de payer par carte, chèque ou en espèces. - -*Mode de retrait des places* -Lors du paiement, nous vous donnerons les places physiques que nous possédons, et vous indiquerons -quelles places sont sur listing, à retirer le soir même de la représentation. Nous vous enverrons un mail de rappel quelques jours avant les spectacles. - -Nous vous rappelons que l'obtention de places du BdA vous engage à -respecter les règles de fonctionnement : -http://www.cof.ens.fr/bda/?page_id=1370 -Le système de revente des places via les mails BdA-revente est accessible -directement sur votre compte GestioCOF. - -En vous souhaitant de belles représentations, --- -Le (nouveau) Bureau des Arts -(Thomas, Caroline, Antonin, Cécile, Hugo) -""" - attribs_text = "" - name = member.user.get_full_name() - for attrib in attribs: - attribs_text += u"- 1 place pour %s\n" % attrib - mail = mail % (name, attribs_text) - - send_mail ("[BdA] Résultats du tirage au sort", mail, - "bda@ens.fr", [member.user.email], - fail_silently = True) - count = len(queryset.all()) - if count == 1: - message_bit = u"1 membre a" - plural = "" - else: - message_bit = u"%d membres ont" % count - plural = "s" - self.message_user(request, u"%s été informé%s avec succès." % (message_bit, plural)) - send_attribs.short_description = u"Envoyer les résultats par mail" - -class AttributionAdmin(admin.ModelAdmin): - def paid(self, obj): - return obj.participant.paid - paid.short_description = 'A payé' - paid.boolean = True - list_display = ("id", "spectacle", "participant", "given", "paid") - search_fields = ('spectacle__title', 'participant__user__username', 'participant__user__first_name', 'participant__user__last_name') - -import autocomplete_light -class ChoixSpectacleAdmin(admin.ModelAdmin): - form = autocomplete_light.modelform_factory(ChoixSpectacle, exclude=[]) - list_display = ("participant", "spectacle", "priority", "double", "autoquit") - list_filter = ("double", "autoquit") - search_fields = ('participant__user__username', 'participant__user__first_name', 'participant__user__last_name') - -class SpectacleAdmin(admin.ModelAdmin): - model = Spectacle - list_display = ("title", "date", "location", "slots", "price") - list_filter = ("location",) - search_fields = ("title", "location__name") - -admin.site.register(Spectacle, SpectacleAdmin) -admin.site.register(Salle) -admin.site.register(Participant, ParticipantAdmin) -admin.site.register(Attribution, AttributionAdmin) -admin.site.register(ChoixSpectacle, ChoixSpectacleAdmin) diff --git a/bda2/algorithm.py b/bda2/algorithm.py deleted file mode 100644 index 623f9756..00000000 --- a/bda2/algorithm.py +++ /dev/null @@ -1,104 +0,0 @@ -# coding: utf-8 - -from django.conf import settings -from django.db.models import Max - -import random - -class Algorithm(object): - - shows = None - ranks = None - origranks = None - double = None - - def __init__(self, shows, members, choices): - """Initialisation : - - on aggrège toutes les demandes pour chaque spectacle dans - show.requests - - on crée des tables de demandes pour chaque personne, afin de - pouvoir modifier les rankings""" - self.max_group = 2 * choices.aggregate(Max('priority'))['priority__max'] - self.shows = [] - showdict = {} - for show in shows: - show.nrequests = 0 - showdict[show] = show - show.requests = [] - self.shows.append(show) - self.ranks = {} - self.origranks = {} - self.choices = {} - next_rank = {} - member_shows = {} - for member in members: - self.ranks[member] = {} - self.choices[member] = {} - next_rank[member] = 1 - member_shows[member] = {} - for choice in choices: - member = choice.participant - if choice.spectacle in member_shows[member]: continue - else: member_shows[member][choice.spectacle] = True - showdict[choice.spectacle].requests.append(member) - showdict[choice.spectacle].nrequests += 2 if choice.double else 1 - self.ranks[member][choice.spectacle] = next_rank[member] - next_rank[member] += 2 if choice.double else 1 - self.choices[member][choice.spectacle] = choice - for member in members: - self.origranks[member] = dict(self.ranks[member]) - - def IncrementRanks(self, member, currank, increment = 1): - for show in self.ranks[member]: - if self.ranks[member][show] > currank: - self.ranks[member][show] -= increment - - def appendResult(self, l, member, show): - l.append((member, - self.ranks[member][show], - self.origranks[member][show], - self.choices[member][show].double)) - - """ - Pour les 2 Walkyries: c'est Sandefer - - Pour les 4 places pour l'Oratorio c'est Maxence Arutkin - """ - - def __call__(self, seed): - random.seed(seed) - results = [] - shows = sorted(self.shows, key = lambda x: float(x.nrequests) / x.slots, reverse = True) - for show in shows: - # On regroupe tous les gens ayant le même rang - groups = dict([(i, []) for i in range(1, self.max_group + 1)]) - for member in show.requests: - if self.ranks[member][show] == 0: - raise RuntimeError, (member, show.title) - groups[self.ranks[member][show]].append(member) - # On passe à l'attribution - winners = [] - losers = [] - for i in range(1, self.max_group + 1): - group = list(groups[i]) - random.shuffle(group) - for member in group: - if self.choices[member][show].double: # double - if len(winners) + 1 < show.slots: - self.appendResult(winners, member, show) - self.appendResult(winners, member, show) - elif not self.choices[member][show].autoquit and len(winners) < show.slots: - self.appendResult(winners, member, show) - self.appendResult(losers, member, show) - else: - self.appendResult(losers, member, show) - self.appendResult(losers, member, show) - self.IncrementRanks(member, i, 2) - else: # simple - if len(winners) < show.slots: - self.appendResult(winners, member, show) - else: - self.appendResult(losers, member, show) - self.IncrementRanks(member, i) - results.append((show,winners,losers)) - return results diff --git a/bda2/autocomplete_light_registry.py b/bda2/autocomplete_light_registry.py deleted file mode 100644 index 3254b3c8..00000000 --- a/bda2/autocomplete_light_registry.py +++ /dev/null @@ -1,9 +0,0 @@ -import autocomplete_light - -from bda.models import Participant, Spectacle - -autocomplete_light.register(Participant, search_fields=('user__username','user__first_name','user__last_name'), - autocomplete_js_attributes={'placeholder': 'participant...'}) - -autocomplete_light.register(Spectacle, search_fields=('title',), - autocomplete_js_attributes={'placeholder': 'spectacle...'}) diff --git a/bda2/difftobda b/bda2/difftobda deleted file mode 100644 index 3e6686e5..00000000 --- a/bda2/difftobda +++ /dev/null @@ -1,421 +0,0 @@ -Only in .: .admin.py.swp -Binary files ../bda/__init__.pyc and ./__init__.pyc differ -diff -p -u -r ../bda/admin.py ./admin.py ---- ../bda/admin.py 2013-12-17 10:19:56.000000000 +0100 -+++ ./admin.py 2012-12-08 22:31:46.000000000 +0100 -@@ -4,8 +4,8 @@ from django.core.mail import send_mail - from django.contrib.contenttypes.models import ContentType - - from django.contrib import admin --from django.db.models import Sum, Count --from bda.models import Spectacle, Salle, Participant, ChoixSpectacle, Attribution -+from django.db.models import Sum -+from bda2.models import Spectacle, Salle, Participant, ChoixSpectacle, Attribution - - class ChoixSpectacleInline(admin.TabularInline): - model = ChoixSpectacle -@@ -17,18 +17,13 @@ class AttributionInline(admin.TabularInl - class ParticipantAdmin(admin.ModelAdmin): - #inlines = [ChoixSpectacleInline] - inlines = [AttributionInline] -- def queryset(self, request): -- return Participant.objects.annotate(nb_places = Count('attributions'), -- total = Sum('attributions__price')) - def nb_places(self, obj): -- return obj.nb_places -- nb_places.admin_order_field = "nb_places" -+ return len(obj.attribution_set.all()) - nb_places.short_description = "Nombre de places" - def total(self, obj): -- tot = obj.total -+ tot = obj.attributions.aggregate(total = Sum('price'))['total'] - if tot: return u"%.02f €" % tot - else: return u"0 €" -- total.admin_order_field = "total" - total.short_description = "Total à payer" - list_display = ("user", "nb_places", "total", "paid", "paymenttype") - list_filter = ("paid",) -@@ -61,7 +56,7 @@ Voici tes choix de spectacles tels que n - Artistiquement, - Le BdA""" - send_mail ("Choix de spectacles (BdA du COF)", mail, -- "bda@ens.fr", [member.user.email], -+ "bda@clipper.ens.fr", [member.user.email], - fail_silently = True) - count = len(queryset.all()) - if count == 1: -@@ -86,48 +81,41 @@ pour les spectacles suivants : - %s - - *Paiement* --L'intégralité de ces places de spectacles est à régler à partir du jeudi --10 octobre et AVANT le mercredi 23 octobre, au bureau du COF pendant les --heures de permanences (du lundi au vendredi entre 12h et 14h, et entre 18h --et 20h). Des facilités de paiement sont bien évidemment possibles : nous --pouvons ne pas encaisser le chèque immédiatement, ou bien découper votre --paiement en deux fois. -+Ces spectacles sont à régler avant le lundi 17 décembre, pendant les -+heures de permanences du COF (tous les jours de la semaine entre 12h et -+14h, et entre 18h et 20h). Des facilités de paiement sont bien évidemment -+possibles (encaissement échelonné des chèques). - - *Mode de retrait des places* --Au moment du paiement, une enveloppe vous sera remise, contenant les --places pour l'Opéra de Paris, pour les premiers spectacles de la Comédie --française, certains spectacles du Châtelet et du Théâtre de la Ville. -- --Pour les concerts Radio France, le Théâtre des Champs-Élysées, le théâtre --du Rond-Point, le théâtre de la Colline, le théâtre de l'Athénée, l'IRCAM, --la Cité de la musique et le 104, le Studio-Théâtre de la Comédie --française, les places seront nominatives et à retirer au théâtre le soir --de la représentation au moins une demi-heure avant le début du spectacle. -- --Pour le théâtre de l'Odéon, la salle Richelieu le théâtre du Vieux --colombier de la Comédie française, certains spectacles du théâtre de la --Ville et du théâtre de Châtelet ainsi que pour le théâtre de Chaillot, les --places seront distribuées environ une semaine avant la représentation (un --mail vous en avertira). -- --Nous vous rappelons que l'obtention de places du BdA vous engage à --respecter les règles de fonctionnement : --http://www.cof.ens.fr/bda/?page_id=1370 --Le système de revente des places via les mails BdA-revente sera très --prochainement disponible, directement sur votre compte GestioCOF. -+Pour l'Opéra de Paris, le théâtre de la Colline et le théâtre du Châtelet, -+les places sont à retirer au COF le jour du paiement. -+ -+Pour les concerts Radio France, le théâtre des Champs-Élysées, l'IRCAM -+et le 104, les places seront nominatives et à retirer à la salle le soir de -+la représentation au moins une demi-heure avant le début du spectacle. -+ -+Pour le théâtre de l'Odéon, la Comédie Française, le théâtre de la Ville, -+le théâtre de Chaillot, les places seront distribuées dans vos -+casiers environ une semaine avant la représentation (un mail vous en -+avertira). -+ -+Dans tous les cas, n'hésitez pas à nous contacter si vous avez un doute ! -+ -+Nous profitons aussi de ce message pour vous annoncer qu'un festival de -+courts métrages aura lieu le 21 décembre dans l'école. -+Plus d'informations : http://www.cof.ens.fr/bda/courts. -+ -+Culturellement vôtre, - --En vous souhaitant de très beaux spectacles tout au long de l'année, - -- --Le Bureau des Arts --(Chloé, Emilie, Jaime, Maxime, Olivier) --""" -+Le BdA""" - attribs_text = "" - name = member.user.get_full_name() - for attrib in attribs: - attribs_text += u"- 1 place pour %s\n" % attrib - mail = mail % (name, attribs_text) -- send_mail ("Résultats du tirage au sort", mail, -- "bda@ens.fr", [member.user.email], -+ send_mail ("Places de spectacle (BdA du COF)", mail, -+ "bda@clipper.ens.fr", [member.user.email], - fail_silently = True) - count = len(queryset.all()) - if count == 1: -@@ -154,13 +142,7 @@ class ChoixSpectacleAdmin(admin.ModelAdm - list_filter = ("double", "autoquit") - search_fields = ('participant__user__username', 'participant__user__first_name', 'participant__user__last_name') - --class SpectacleAdmin(admin.ModelAdmin): -- model = Spectacle -- list_display = ("title", "date", "location", "slots", "price") -- list_filter = ("location",) -- search_fields = ("title", "location__name") -- --admin.site.register(Spectacle, SpectacleAdmin) -+admin.site.register(Spectacle) - admin.site.register(Salle) - admin.site.register(Participant, ParticipantAdmin) - admin.site.register(Attribution, AttributionAdmin) -diff -p -u -r ../bda/algorithm.py ./algorithm.py ---- ../bda/algorithm.py 2013-10-07 14:08:44.000000000 +0200 -+++ ./algorithm.py 2012-10-31 23:01:48.000000000 +0100 -@@ -29,24 +29,25 @@ class Algorithm(object): - self.ranks = {} - self.origranks = {} - self.choices = {} -- next_rank = {} -- member_shows = {} - for member in members: -- self.ranks[member] = {} -- self.choices[member] = {} -- next_rank[member] = 1 -- member_shows[member] = {} -- for choice in choices: -- member = choice.participant -- if choice.spectacle in member_shows[member]: continue -- else: member_shows[member][choice.spectacle] = True -- showdict[choice.spectacle].requests.append(member) -- showdict[choice.spectacle].nrequests += 2 if choice.double else 1 -- self.ranks[member][choice.spectacle] = next_rank[member] -- next_rank[member] += 2 if choice.double else 1 -- self.choices[member][choice.spectacle] = choice -- for member in members: -- self.origranks[member] = dict(self.ranks[member]) -+ ranks = {} -+ member_choices = {} -+ member_shows = {} -+ #next_priority = 1 -+ next_rank = 1 -+ for choice in member.choixspectacle_set.order_by('priority').all(): -+ if choice.spectacle in member_shows: continue -+ else: member_shows[choice.spectacle] = True -+ #assert choice.priority == next_priority -+ #next_priority += 1 -+ showdict[choice.spectacle].requests.append(member) -+ showdict[choice.spectacle].nrequests += 2 if choice.double else 1 -+ ranks[choice.spectacle] = next_rank -+ next_rank += 2 if choice.double else 1 -+ member_choices[choice.spectacle] = choice -+ self.ranks[member] = ranks -+ self.choices[member] = member_choices -+ self.origranks[member] = dict(ranks) - - def IncrementRanks(self, member, currank, increment = 1): - for show in self.ranks[member]: -Only in ../bda: algorithm.pyc -Only in .: difftobda -diff -p -u -r ../bda/models.py ./models.py ---- ../bda/models.py 2013-10-10 10:44:43.000000000 +0200 -+++ ./models.py 2012-10-31 23:12:56.000000000 +0100 -@@ -1,7 +1,5 @@ - # coding: utf-8 - --import calendar -- - from django.db import models - from django.contrib.auth.models import User - from django.utils.translation import ugettext_lazy as _ -@@ -19,7 +17,7 @@ class Spectacle (models.Model): - date = models.DateTimeField ("Date & heure") - location = models.ForeignKey(Salle) - description = models.TextField ("Description", blank = True) -- slots_description = models.TextField ("Description des places", blank = True) -+ #slots_description = models.TextField ("Description des places", blank = True) - price = models.FloatField("Prix d'une place", blank = True) - slots = models.IntegerField ("Places") - priority = models.IntegerField ("Priorité", default = 1000) -@@ -31,14 +29,11 @@ class Spectacle (models.Model): - def __repr__ (self): - return u"[%s]" % self.__unicode__() - -- def timestamp(self): -- return "%d" % calendar.timegm(self.date.utctimetuple()) -- - def date_no_seconds(self): - return self.date.strftime('%d %b %Y %H:%M') - - def __unicode__ (self): -- return u"%s - %s, %s, %.02f€" % (self.title, self.date_no_seconds(), self.location, self.price) -+ return u"%s - %s @ %s, %.02f€" % (self.title, self.date_no_seconds(), self.location, self.price) - - PAYMENT_TYPES = ( - ("cash",u"Cash"), -@@ -48,7 +43,7 @@ PAYMENT_TYPES = ( - ) - - class Participant (models.Model): -- user = models.ForeignKey(User, unique = True) -+ user = models.ForeignKey(User, unique = True, related_name = "participants2") - choices = models.ManyToManyField(Spectacle, through = "ChoixSpectacle", related_name = "chosen_by") - attributions = models.ManyToManyField(Spectacle, through = "Attribution", related_name = "attributed_to") - paid = models.BooleanField (u"A payé", default = False) -Binary files ../bda/models.pyc and ./models.pyc differ -diff -p -u -r ../bda/views.py ./views.py ---- ../bda/views.py 2014-01-04 21:37:15.000000000 +0100 -+++ ./views.py 2013-02-12 22:03:38.000000000 +0100 -@@ -1,23 +1,19 @@ - # coding: utf-8 - --from django.contrib.auth.models import User - from django.shortcuts import render, get_object_or_404 - from django.contrib.auth.decorators import login_required - from django.db import models - from django.http import Http404 - from django import forms - from django.forms.models import inlineformset_factory, BaseInlineFormSet --from django.core import serializers --import hashlib - - from django.core.mail import send_mail - --from datetime import datetime - import time - - from gestioncof.decorators import cof_required, buro_required --from bda.models import Spectacle, Participant, ChoixSpectacle, Attribution --from bda.algorithm import Algorithm -+from bda2.models import Spectacle, Participant, ChoixSpectacle, Attribution -+from bda2.algorithm import Algorithm - - class BaseBdaFormSet(BaseInlineFormSet): - def clean(self): -@@ -37,7 +33,7 @@ class BaseBdaFormSet(BaseInlineFormSet): - raise forms.ValidationError("Vous ne pouvez pas vous inscrire deux fois pour le même spectacle.") - spectacles.append(spectacle) - --@cof_required -+@buro_required - def etat_places(request): - spectacles1 = ChoixSpectacle.objects.all().values('spectacle','spectacle__title').annotate(total = models.Count('spectacle')) - spectacles2 = ChoixSpectacle.objects.filter(double = True).all().values('spectacle','spectacle__title').annotate(total = models.Count('spectacle')) -@@ -46,93 +42,47 @@ def etat_places(request): - total = 0 - for spectacle in spectacles: - spectacle.total = 0 -- spectacle.ratio = -1.0 - spectacles_dict[spectacle.id] = spectacle - for spectacle in spectacles1: - spectacles_dict[spectacle["spectacle"]].total += spectacle["total"] -- spectacles_dict[spectacle["spectacle"]].ratio = spectacles_dict[spectacle["spectacle"]].total/float(spectacles_dict[spectacle["spectacle"]].slots) - total += spectacle["total"] - for spectacle in spectacles2: - spectacles_dict[spectacle["spectacle"]].total += spectacle["total"] -- spectacles_dict[spectacle["spectacle"]].ratio = spectacles_dict[spectacle["spectacle"]].total/float(spectacles_dict[spectacle["spectacle"]].slots) - total += spectacle["total"] - return render(request, "etat-places.html", {"spectacles": spectacles, "total": total}) - --def _hash_queryset(queryset): -- data = serializers.serialize("json", queryset) -- hasher = hashlib.sha256() -- hasher.update(data) -- return hasher.hexdigest() -- --@cof_required --def places(request): -- participant, created = Participant.objects.get_or_create(user = request.user) -- places = participant.attribution_set.order_by("spectacle__date", "spectacle").all() -- total = sum([place.spectacle.price for place in places]) -- filtered_places = [] -- places_dict = {} -- spectacles = [] -- dates = [] -- warning = False -- for place in places: -- if place.spectacle in spectacles: -- places_dict[place.spectacle].double = True -- else: -- place.double = False -- places_dict[place.spectacle] = place -- spectacles.append(place.spectacle) -- filtered_places.append(place) -- date = place.spectacle.date.date() -- if date in dates: -- warning = True -- else: -- dates.append(date) -- return render(request, "resume_places.html", -- {"participant": participant, -- "places": filtered_places, -- "total": total, -- "warning": warning}) -- - @cof_required - def inscription(request): -- if datetime.now() > datetime(2013, 10, 6, 23, 59): -- participant, created = Participant.objects.get_or_create(user = request.user) -- choices = participant.choixspectacle_set.order_by("priority").all() -- return render(request, "resume_inscription.html", {"error_title": "C'est fini !", "error_description": u"Tirage au sort le 7 octobre !", "choices": choices}) -+ if time.time() > 1354921200: -+ return render(request, "error.html", {"error_title": "C'est fini !", "error_description": u"Tirage au sort le 6 octobre dans la soirée "}) - BdaFormSet = inlineformset_factory(Participant, ChoixSpectacle, fields = ("spectacle","double","autoquit","priority",), formset = BaseBdaFormSet) - participant, created = Participant.objects.get_or_create(user = request.user) - success = False -- stateerror = False - if request.method == "POST": -- dbstate = _hash_queryset(participant.choixspectacle_set.all()) -- if "dbstate" in request.POST and dbstate != request.POST["dbstate"]: -- stateerror = True -+ formset = BdaFormSet(request.POST, instance = participant) -+ if formset.is_valid(): -+ #ChoixSpectacle.objects.filter(participant = participant).delete() -+ formset.save() -+ success = True - formset = BdaFormSet(instance = participant) -- else: -- formset = BdaFormSet(request.POST, instance = participant) -- if formset.is_valid(): -- #ChoixSpectacle.objects.filter(participant = participant).delete() -- formset.save() -- success = True -- formset = BdaFormSet(instance = participant) - else: - formset = BdaFormSet(instance = participant) -- dbstate = _hash_queryset(participant.choixspectacle_set.all()) - total_price = 0 - for choice in participant.choixspectacle_set.all(): - total_price += choice.spectacle.price - if choice.double: total_price += choice.spectacle.price -- return render(request, "inscription-bda.html", {"formset": formset, "success": success, "total_price": total_price, "dbstate": dbstate, "stateerror": stateerror}) -+ return render(request, "inscription-bda.html", {"formset": formset, "success": success, "total_price": total_price}) -+ -+Spectacle.deficit = lambda x: (x.slots-x.nrequests)*x.price - - def do_tirage(request): - form = TokenForm(request.POST) - if not form.is_valid(): - return tirage(request) -- start = time.time() - data = {} -- shows = Spectacle.objects.select_related().all() -+ shows = Spectacle.objects.all() - members = Participant.objects.all() -- choices = ChoixSpectacle.objects.order_by('participant', 'priority').select_related().all() -+ choices = ChoixSpectacle.objects.all() - algo = Algorithm(shows, members, choices) - results = algo(form.cleaned_data["token"]) - total_slots = 0 -@@ -149,8 +99,8 @@ def do_tirage(request): - total_sold = 0 - total_deficit = 0 - opera_deficit = 0 -- for (show, members, _) in results: -- deficit = (show.slots - len(members)) * show.price -+ for show in shows: -+ deficit = show.deficit() - total_sold += show.slots * show.price - if deficit >= 0: - if u"Opéra" in show.location.name: -@@ -159,23 +109,18 @@ def do_tirage(request): - data["total_sold"] = total_sold - total_deficit - data["total_deficit"] = total_deficit - data["opera_deficit"] = opera_deficit -- data["duration"] = time.time() - start - if request.user.is_authenticated(): - members2 = {} -- members_uniq = {} # Participant objects are not shared accross spectacle results, -- # So assign a single object for each Participant id - for (show, members, _) in results: - for (member, _, _, _) in members: -- if member.id not in members_uniq: -- members_uniq[member.id] = member -+ if member not in members2: - members2[member] = [] - member.total = 0 -- member = members_uniq[member.id] - members2[member].append(show) - member.total += show.price - members2 = members2.items() - data["members2"] = sorted(members2, key = lambda m: m[0].user.last_name) -- if False and request.user.username in ["seguin", "harazi"]: -+ if False and request.user.username == "seguin": - Attribution.objects.all().delete() - for (show, members, _) in results: - for (member, _, _, _) in members: -@@ -220,7 +165,7 @@ Je souhaite revendre %s pour %s le %s (% - Contactez moi par email si vous êtes intéressés ! - - %s (%s)""" % (places, spectacle.title, spectacle.date_no_seconds(), spectacle.location, spectacle.price, request.user.get_full_name(), request.user.email) -- send_mail("%s" % spectacle, mail, -+ send_mail("Revente de place: %s" % spectacle, mail, - request.user.email, ["bda-revente@lists.ens.fr"], - fail_silently = True) - return render(request, "bda-success.html", {"show": spectacle, "places": places}) -Only in .: views.py~ diff --git a/bda2/forms.py b/bda2/forms.py deleted file mode 100644 index 930e184a..00000000 --- a/bda2/forms.py +++ /dev/null @@ -1,39 +0,0 @@ -# coding: utf-8 - -from django import forms -from django.forms.models import inlineformset_factory, BaseInlineFormSet -from bda2.models import Spectacle, Participant, ChoixSpectacle, Attribution - -class BaseBdaFormSet(BaseInlineFormSet): - def clean(self): - """Checks that no two articles have the same title.""" - super(BaseBdaFormSet, self).clean() - if any(self.errors): - # Don't bother validating the formset unless each form is valid on its own - return - spectacles = [] - for i in range(0, self.total_form_count()): - form = self.forms[i] - if not form.cleaned_data: - continue - spectacle = form.cleaned_data['spectacle'] - delete = form.cleaned_data['DELETE'] - if not delete and spectacle in spectacles: - raise forms.ValidationError("Vous ne pouvez pas vous inscrire deux fois pour le même spectacle.") - spectacles.append(spectacle) - -class TokenForm(forms.Form): - token = forms.CharField(widget = forms.widgets.Textarea()) - -class SpectacleModelChoiceField(forms.ModelChoiceField): - def label_from_instance(self, obj): - return u"%s le %s (%s) à %.02f€" % (obj.title, obj.date_no_seconds(), obj.location, obj.price) - -class ResellForm(forms.Form): - count = forms.ChoiceField(choices = (("1","1"),("2","2"),)) - spectacle = SpectacleModelChoiceField(queryset = Spectacle.objects.none()) - - def __init__(self, participant, *args, **kwargs): - super(ResellForm, self).__init__(*args, **kwargs) - self.fields['spectacle'].queryset = participant.attributions.all().distinct() - diff --git a/bda2/migrations/0001_initial.py b/bda2/migrations/0001_initial.py deleted file mode 100644 index 450e1607..00000000 --- a/bda2/migrations/0001_initial.py +++ /dev/null @@ -1,109 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models -from django.conf import settings - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='Attribution', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('given', models.BooleanField(default=False, verbose_name='Donn\xe9e')), - ], - ), - migrations.CreateModel( - name='ChoixSpectacle', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('priority', models.PositiveIntegerField(verbose_name=b'Priorit\xc3\xa9')), - ('double', models.BooleanField(default=False, verbose_name=b'Deux places1')), - ('autoquit', models.BooleanField(default=False, verbose_name=b'Abandon2')), - ], - options={ - 'ordering': ('priority',), - 'verbose_name': 'voeu', - 'verbose_name_plural': 'voeux', - }, - ), - migrations.CreateModel( - name='Participant', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('paid', models.BooleanField(default=False, verbose_name='A pay\xe9')), - ('paymenttype', models.CharField(blank=True, max_length=6, verbose_name='Moyen de paiement', choices=[(b'cash', 'Cash'), (b'cb', b'CB'), (b'cheque', 'Ch\xe8que'), (b'autre', 'Autre')])), - ], - ), - migrations.CreateModel( - name='Salle', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField(max_length=300, verbose_name=b'Nom')), - ('address', models.TextField(verbose_name=b'Adresse')), - ], - ), - migrations.CreateModel( - name='Spectacle', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('title', models.CharField(max_length=300, verbose_name=b'Titre')), - ('date', models.DateTimeField(verbose_name=b'Date & heure')), - ('description', models.TextField(verbose_name=b'Description', blank=True)), - ('slots_description', models.TextField(verbose_name=b'Description des places', blank=True)), - ('price', models.FloatField(verbose_name=b"Prix d'une place", blank=True)), - ('slots', models.IntegerField(verbose_name=b'Places')), - ('priority', models.IntegerField(default=1000, verbose_name=b'Priorit\xc3\xa9')), - ('location', models.ForeignKey(to='bda2.Salle')), - ], - options={ - 'ordering': ('priority', 'date', 'title'), - 'verbose_name': 'Spectacle', - }, - ), - migrations.AddField( - model_name='participant', - name='attributions', - field=models.ManyToManyField(related_name='attributed_to', through='bda2.Attribution', to='bda2.Spectacle'), - ), - migrations.AddField( - model_name='participant', - name='choices', - field=models.ManyToManyField(related_name='chosen_by', through='bda2.ChoixSpectacle', to='bda2.Spectacle'), - ), - migrations.AddField( - model_name='participant', - name='user', - field=models.OneToOneField(related_name='participants2', to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='choixspectacle', - name='participant', - field=models.ForeignKey(to='bda2.Participant'), - ), - migrations.AddField( - model_name='choixspectacle', - name='spectacle', - field=models.ForeignKey(related_name='participants', to='bda2.Spectacle'), - ), - migrations.AddField( - model_name='attribution', - name='participant', - field=models.ForeignKey(to='bda2.Participant'), - ), - migrations.AddField( - model_name='attribution', - name='spectacle', - field=models.ForeignKey(related_name='attribues', to='bda2.Spectacle'), - ), - migrations.AlterUniqueTogether( - name='choixspectacle', - unique_together=set([('participant', 'spectacle')]), - ), - ] diff --git a/bda2/migrations/__init__.py b/bda2/migrations/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/bda2/models.py b/bda2/models.py deleted file mode 100644 index c9fefe6e..00000000 --- a/bda2/models.py +++ /dev/null @@ -1,78 +0,0 @@ -# coding: utf-8 - -import calendar - -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 - -class Salle (models.Model): - name = models.CharField ("Nom", max_length = 300) - address = models.TextField ("Adresse") - - def __unicode__ (self): - return self.name - -class Spectacle (models.Model): - title = models.CharField ("Titre", max_length = 300) - date = models.DateTimeField ("Date & heure") - location = models.ForeignKey(Salle) - description = models.TextField ("Description", blank = True) - slots_description = models.TextField ("Description des places", blank = True) - price = models.FloatField("Prix d'une place", blank = True) - slots = models.IntegerField ("Places") - priority = models.IntegerField ("Priorité", default = 1000) - - class Meta: - verbose_name = "Spectacle" - ordering = ("priority", "date","title",) - - def __repr__ (self): - return u"[%s]" % self.__unicode__() - - def timestamp(self): - return "%d" % calendar.timegm(self.date.utctimetuple()) - - def date_no_seconds(self): - return self.date.strftime('%d %b %Y %H:%M') - - def __unicode__ (self): - return u"%s - %s, %s, %.02f€" % (self.title, self.date_no_seconds(), self.location, self.price) - -PAYMENT_TYPES = ( -("cash",u"Cash"), -("cb","CB"), -("cheque",u"Chèque"), -("autre",u"Autre"), -) - -class Participant (models.Model): - user = models.OneToOneField(User, related_name = "participants2") - choices = models.ManyToManyField(Spectacle, through = "ChoixSpectacle", related_name = "chosen_by") - attributions = models.ManyToManyField(Spectacle, through = "Attribution", related_name = "attributed_to") - paid = models.BooleanField (u"A payé", default = False) - paymenttype = models.CharField(u"Moyen de paiement", max_length = 6, choices = PAYMENT_TYPES, blank = True) - - def __unicode__ (self): - return u"%s" % (self.user) - -class ChoixSpectacle (models.Model): - participant = models.ForeignKey(Participant) - spectacle = models.ForeignKey(Spectacle, related_name = "participants") - priority = models.PositiveIntegerField("Priorité") - double = models.BooleanField("Deux places1",default=False) - autoquit = models.BooleanField("Abandon2",default=False) - class Meta: - ordering = ("priority",) - unique_together = (("participant", "spectacle",),) - verbose_name = "voeu" - verbose_name_plural = "voeux" - -class Attribution (models.Model): - participant = models.ForeignKey(Participant) - spectacle = models.ForeignKey(Spectacle, related_name = "attribues") - given = models.BooleanField(u"Donnée", default = False) - - def __unicode__ (self): - return u"%s -- %s" % (self.participant, self.spectacle) diff --git a/bda2/static/css/bda.css b/bda2/static/css/bda.css deleted file mode 100644 index 8851eeee..00000000 --- a/bda2/static/css/bda.css +++ /dev/null @@ -1,10 +0,0 @@ -form#tokenform {text-align: center; font-size: 2em;} -label {margin-right: 10px; vertical-align: top;} -form#tokenform textarea {font-size: 2em; width: 350px; height: 200px; font-family: 'Droif Serif', serif;} -input {width: 400px; font-size: 2em;} -ul.losers {display: inline; margin: 0; padding: 0;} -ul.losers li {display: inline;} -span.details {font-size: 0.7em;} -table {border: 1px solid black; border-collapse: collapse;} -td {border: 1px solid black; padding: 2px;} -.attribresult {margin: 10px 0px;} diff --git a/bda2/templates/bda-attrib-extra.html b/bda2/templates/bda-attrib-extra.html deleted file mode 100644 index c2c266d2..00000000 --- a/bda2/templates/bda-attrib-extra.html +++ /dev/null @@ -1,27 +0,0 @@ -{% extends "bda-attrib.html" %} - -{% block extracontent %} - -

Attribution (détails)

-

Token : {{ token }}

-

Placés : {{ total_slots }} ; Déçus : {{ total_losers }}

- -
-{% for member, shows in members2 %} - - - - - - -{% for show in shows %} - - - - - - -{% endfor %} -{% endfor %} -
{{ member.user.get_full_name }}{{ member.user.email }}Total: {{ member.total }}€
{{ show }}
-{% endblock %} diff --git a/bda2/templates/bda-attrib.html b/bda2/templates/bda-attrib.html deleted file mode 100644 index 8390bd03..00000000 --- a/bda2/templates/bda-attrib.html +++ /dev/null @@ -1,41 +0,0 @@ -{% extends "base_title.html" %} -{% load staticfiles %} - -{% block extra_head %} - -{% endblock %} - -{% block realcontent %} - -

Attribution

-

Token : {{ token }}

-

Placés : {{ total_slots }} ; Déçus : {{ total_losers }}

-{% if user.profile.is_buro %}

Déficit total: {{ total_deficit }} €, Opéra: {{ opera_deficit }} €, Attribué: {{ total_sold }} €

{% endif %} - -{% for show, members, losers in results %} -
-

{{ show.title }} - {{ show.date_no_seconds }} @ {{ show.location }}

-

-{{ show.nrequests }} demandes pour {{ show.slots }} places -{{ show.price }}€ par place{% if user.profile.is_buro and show.nrequests < show.slots %}, {{ show.deficit }}€ de déficit{% endif %} -

-Places : - -Déçus : -{% if not losers %}/{% else %} - -{% endif %} -
-{% endfor %} -{% block extracontent %} -{% endblock %} -{% endblock %} diff --git a/bda2/templates/bda-emails.html b/bda2/templates/bda-emails.html deleted file mode 100644 index 39744ae1..00000000 --- a/bda2/templates/bda-emails.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends "base_title.html" %} - -{% block realcontent %} -

{{ spectacle }}

- -{% endblock %} diff --git a/bda2/templates/bda-notpaid.html b/bda2/templates/bda-notpaid.html deleted file mode 100644 index 10b272d8..00000000 --- a/bda2/templates/bda-notpaid.html +++ /dev/null @@ -1,6 +0,0 @@ -{% extends "base_title.html" %} - -{% block realcontent %} -

Nope

-

Avant de revendre des places, il faut aller les payer !

-{% endblock %} diff --git a/bda2/templates/bda-revente.html b/bda2/templates/bda-revente.html deleted file mode 100644 index 82ed80fe..00000000 --- a/bda2/templates/bda-revente.html +++ /dev/null @@ -1,26 +0,0 @@ -{% extends "base_title.html" %} -{% load staticfiles %} - -{% block extra_head %} - -{% endblock %} - -{% block realcontent %} - -

Revente de place

-
- {% csrf_token %} -{% if form.spectacle.errors %}{% endif %} -

- -Bonjour,
-
-Je souhaite revendre {{ form.count }} place(s) pour {{ form.spectacle }}.
-Contactez-moi par email si vous êtes intéressé !
-
-{{ user.get_full_name }} ({{ user.email }}) -
-

- -
-{% endblock %} diff --git a/bda2/templates/bda-success.html b/bda2/templates/bda-success.html deleted file mode 100644 index ebcc87a7..00000000 --- a/bda2/templates/bda-success.html +++ /dev/null @@ -1,12 +0,0 @@ -{% extends "base_title.html" %} -{% load staticfiles %} - -{% block extra_head %} - -{% endblock %} - -{% block realcontent %} - -

Revente de place

-

Votre offre de revente de {{ places }} pour {{ show.title }} le {{ show.date_no_seconds }} ({{ show.location }}) à {{ show.price }}€ a bien été envoyée.

-{% endblock %} diff --git a/bda2/templates/bda-token.html b/bda2/templates/bda-token.html deleted file mode 100644 index cbe72a76..00000000 --- a/bda2/templates/bda-token.html +++ /dev/null @@ -1,13 +0,0 @@ -{% extends "base_title.html" %} - -{% block realcontent %} -

Tirage au sort du BdA

-
- {% csrf_token %} - La graine : -
- {{ form.token }} -
- -
-{% endblock %} diff --git a/bda2/templates/bda-unpaid.html b/bda2/templates/bda-unpaid.html deleted file mode 100644 index 5596dd82..00000000 --- a/bda2/templates/bda-unpaid.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends "base_title.html" %} - -{% block realcontent %} -

Impayés

- -{% endblock %} diff --git a/bda2/templates/etat-places.html b/bda2/templates/etat-places.html deleted file mode 100644 index 086ca648..00000000 --- a/bda2/templates/etat-places.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends "base_title.html" %} - -{% block realcontent %} -

Etat des inscriptions BDA

- - Total : {{ total }} demandes -{% endblock %} diff --git a/bda2/templates/inscription-bda.html b/bda2/templates/inscription-bda.html deleted file mode 100644 index 933f087a..00000000 --- a/bda2/templates/inscription-bda.html +++ /dev/null @@ -1,116 +0,0 @@ -{% extends "base_title.html" %} - -{% block extra_head %} - - - - - -{% endblock %} - -{% block realcontent %} - - -

Inscription au tirage au sort du BDA

- {% if success %} -

Votre inscription a été mise à jour avec succès !

- {% endif %} -
- {% csrf_token %} - {% include "inscription-formset.html" %} - - - - Prix total actuel : {{ total_price }}€ -
-

- 1: demander deux places pour ce spectable
- 2: abandonner une place si impossible d'en obtenir une seconde pour ce spectacle (si vous avez coché l'option Deux places pour ce spectacle)
- 3: cette liste de vœu est ordonnée (du plus important au moins important), pour ajuster la priorité vous pouvez déplacer chaque vœu
-

-
-{% endblock %} diff --git a/bda2/templates/inscription-formset.html b/bda2/templates/inscription-formset.html deleted file mode 100644 index 04b68a6b..00000000 --- a/bda2/templates/inscription-formset.html +++ /dev/null @@ -1,40 +0,0 @@ -{{ formset.non_form_errors.as_ul }} - -{{ formset.management_form }} -{% for form in formset.forms %} - {% if forloop.first %} - - {% for field in form.visible_fields %} - {% if field.name != "DELETE" and field.name != "priority" %} - - {% endif %} - {% endfor %} - - - - {% endif %} - - {% for field in form.visible_fields %} - {% if field.name != "DELETE" and field.name != "priority" %} - - {% endif %} - {% endfor %} - - -{% endfor %} - -
{{ field.label|safe|capfirst }}3
- {% if forloop.first %} - {{ form.non_field_errors }} - {% for hidden in form.hidden_fields %}{{ hidden }}{% endfor %} - {% endif %} - {{ field.errors.as_ul }} - {{ field }} -
- - - - -
-
-
diff --git a/bda2/templates/spectacle_list.html b/bda2/templates/spectacle_list.html deleted file mode 100644 index 956037f8..00000000 --- a/bda2/templates/spectacle_list.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends "base_title.html" %} - -{% block realcontent %} -

Spectacles

- -{% endblock %} diff --git a/bda2/tests.py b/bda2/tests.py deleted file mode 100644 index 501deb77..00000000 --- a/bda2/tests.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -This file demonstrates writing tests using the unittest module. These will pass -when you run "manage.py test". - -Replace this with more appropriate tests for your application. -""" - -from django.test import TestCase - - -class SimpleTest(TestCase): - def test_basic_addition(self): - """ - Tests that 1 + 1 always equals 2. - """ - self.assertEqual(1 + 1, 2) diff --git a/bda2/views.py b/bda2/views.py deleted file mode 100644 index 6e19c7bb..00000000 --- a/bda2/views.py +++ /dev/null @@ -1,214 +0,0 @@ -# coding: utf-8 - -from django.contrib.auth.models import User -from django.shortcuts import render, get_object_or_404 -from django.contrib.auth.decorators import login_required -from django.db import models -from django.http import Http404 -from django.core import serializers -import hashlib - -from django.core.mail import send_mail - -from datetime import datetime -import time - -from gestioncof.decorators import cof_required, buro_required -from bda2.models import Spectacle, Participant, ChoixSpectacle, Attribution -from bda2.algorithm import Algorithm -from bda2.forms import BaseBdaFormSet, TokenForm, ResellForm - -@cof_required -def etat_places(request): - spectacles1 = ChoixSpectacle.objects.all().values('spectacle','spectacle__title').annotate(total = models.Count('spectacle')) - spectacles2 = ChoixSpectacle.objects.filter(double = True).all().values('spectacle','spectacle__title').annotate(total = models.Count('spectacle')) - spectacles = Spectacle.objects.all() - spectacles_dict = {} - total = 0 - for spectacle in spectacles: - spectacle.total = 0 - spectacle.ratio = 0.0 - spectacles_dict[spectacle.id] = spectacle - for spectacle in spectacles1: - spectacles_dict[spectacle["spectacle"]].total += spectacle["total"] - spectacles_dict[spectacle["spectacle"]].ratio = spectacles_dict[spectacle["spectacle"]].total/float(spectacles_dict[spectacle["spectacle"]].slots) - total += spectacle["total"] - for spectacle in spectacles2: - spectacles_dict[spectacle["spectacle"]].total += spectacle["total"] - spectacles_dict[spectacle["spectacle"]].ratio = spectacles_dict[spectacle["spectacle"]].total/float(spectacles_dict[spectacle["spectacle"]].slots) - total += spectacle["total"] - return render(request, "etat-places.html", {"spectacles": spectacles, "total": total}) - -def _hash_queryset(queryset): - data = serializers.serialize("json", queryset) - hasher = hashlib.sha256() - hasher.update(data) - return hasher.hexdigest() - -@cof_required -def places(request): - participant, created = Participant.objects.get_or_create(user = request.user) - places = participant.attribution_set.order_by("spectacle__date", "spectacle").all() - total = sum([place.spectacle.price for place in places]) - filtered_places = [] - places_dict = {} - spectacles = [] - dates = [] - warning = False - for place in places: - if place.spectacle in spectacles: - places_dict[place.spectacle].double = True - else: - place.double = False - places_dict[place.spectacle] = place - spectacles.append(place.spectacle) - filtered_places.append(place) - date = place.spectacle.date.date() - if date in dates: - warning = True - else: - dates.append(date) - return render(request, "resume_places.html", - {"participant": participant, - "places": filtered_places, - "total": total, - "warning": warning}) - -@cof_required -def inscription(request): - if datetime.now() > datetime(2016, 1, 17, 12, 00): - participant, created = Participant.objects.get_or_create(user = request.user) - choices = participant.choixspectacle_set.order_by("priority").all() - return render(request, "resume_inscription.html", {"error_title": "C'est fini !", "error_description": u"Tirage au sort le 17 janvier !", "choices": choices}) - BdaFormSet = inlineformset_factory(Participant, ChoixSpectacle, fields = ("spectacle","double","autoquit","priority",), formset = BaseBdaFormSet) - participant, created = Participant.objects.get_or_create(user = request.user) - success = False - stateerror = False - if request.method == "POST": - dbstate = _hash_queryset(participant.choixspectacle_set.all()) - if "dbstate" in request.POST and dbstate != request.POST["dbstate"]: - stateerror = True - formset = BdaFormSet(instance = participant) - else: - formset = BdaFormSet(request.POST, instance = participant) - if formset.is_valid(): - #ChoixSpectacle.objects.filter(participant = participant).delete() - formset.save() - success = True - formset = BdaFormSet(instance = participant) - else: - formset = BdaFormSet(instance = participant) - dbstate = _hash_queryset(participant.choixspectacle_set.all()) - total_price = 0 - for choice in participant.choixspectacle_set.all(): - total_price += choice.spectacle.price - if choice.double: total_price += choice.spectacle.price - return render(request, "inscription-bda2.html", {"formset": formset, "success": success, "total_price": total_price, "dbstate": dbstate, "stateerror": stateerror}) - -def do_tirage(request): - form = TokenForm(request.POST) - if not form.is_valid(): - return tirage(request) - start = time.time() - data = {} - shows = Spectacle.objects.select_related().all() - members = Participant.objects.all() - choices = ChoixSpectacle.objects.order_by('participant', 'priority').select_related().all() - algo = Algorithm(shows, members, choices) - results = algo(form.cleaned_data["token"]) - total_slots = 0 - total_losers = 0 - for (_, members, losers) in results: - total_slots += len(members) - total_losers += len(losers) - data["total_slots"] = total_slots - data["total_losers"] = total_losers - data["shows"] = shows - data["token"] = form.cleaned_data["token"] - data["members"] = members - data["results"] = results - total_sold = 0 - total_deficit = 0 - opera_deficit = 0 - for (show, members, _) in results: - deficit = (show.slots - len(members)) * show.price - total_sold += show.slots * show.price - if deficit >= 0: - if u"Opéra" in show.location.name: - opera_deficit += deficit - total_deficit += deficit - data["total_sold"] = total_sold - total_deficit - data["total_deficit"] = total_deficit - data["opera_deficit"] = opera_deficit - data["duration"] = time.time() - start - if request.user.is_authenticated(): - members2 = {} - members_uniq = {} # Participant objects are not shared accross spectacle results, - # So assign a single object for each Participant id - 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 - members2 = members2.items() - data["members2"] = sorted(members2, key = lambda m: m[0].user.last_name) - if False and request.user.username in ["seguin", "harazi", "fromherz", "mpepin"]: - Attribution.objects.all().delete() - for (show, members, _) in results: - for (member, _, _, _) in members: - attrib = Attribution(spectacle = show, participant = member) - attrib.save() - return render(request, "bda-attrib-extra.html", data) - else: - return render(request, "bda-attrib.html", data) - -@login_required -def tirage(request): - if request.POST: - form = TokenForm(request.POST) - if form.is_valid(): - return do_tirage(request) - else: - form = TokenForm() - return render(request, "bda-token.html", {"form": form}) - -def do_resell(request, form): - spectacle = form.cleaned_data["spectacle"] - count = form.cleaned_data["count"] - places = "2 places" if count == "2" else "une place" - mail = u"""Bonjour, - -Je souhaite revendre %s pour %s le %s (%s) à %.02f€. -Contactez moi par email si vous êtes intéressés ! - -%s (%s)""" % (places, spectacle.title, spectacle.date_no_seconds(), spectacle.location, spectacle.price, request.user.get_full_name(), request.user.email) - send_mail("%s" % spectacle, mail, - request.user.email, ["bda-revente@lists.ens.fr"], - fail_silently = True) - return render(request, "bda-success.html", {"show": spectacle, "places": places}) - -@login_required -def revente(request): - participant, created = Participant.objects.get_or_create(user = request.user) - if not participant.paid: - return render(request, "bda-notpaid.html", {}) - if request.POST: - form = ResellForm(participant, request.POST) - if form.is_valid(): - return do_resell(request, form) - else: - form = ResellForm(participant) - return render(request, "bda-revente.html", {"form": form}) - -@buro_required -def spectacle(request, spectacle_id): - spectacle = get_object_or_404(Spectacle, id = spectacle_id) - return render(request, "bda-emails.html", {"spectacle": spectacle}) - -@buro_required -def unpaid(request): - return render(request, "bda-unpaid.html", {"unpaid": Participant.objects.filter(paid = False).all()}) diff --git a/bda3/__init__.py b/bda3/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/bda3/admin.py b/bda3/admin.py deleted file mode 100644 index 26b0beed..00000000 --- a/bda3/admin.py +++ /dev/null @@ -1,180 +0,0 @@ -# coding: utf-8 - -from django.core.mail import send_mail -from django.contrib.contenttypes.models import ContentType - -from django.contrib import admin -from django.db.models import Sum, Count -from bda3.models import Spectacle, Salle, Participant, ChoixSpectacle, Attribution - -class ChoixSpectacleInline(admin.TabularInline): - model = ChoixSpectacle - sortable_field_name = "priority" - -class AttributionInline(admin.TabularInline): - model = Attribution - -class ParticipantAdmin(admin.ModelAdmin): - #inlines = [ChoixSpectacleInline] - inlines = [AttributionInline] - def get_queryset(self, request): - return Participant.objects.annotate(nb_places = Count('attributions'), - total = Sum('attributions__price')) - def nb_places(self, obj): - return obj.nb_places - nb_places.admin_order_field = "nb_places" - nb_places.short_description = "Nombre de places" - def total(self, obj): - tot = obj.total - if tot: return u"%.02f €" % tot - else: return u"0 €" - total.admin_order_field = "total" - total.short_description = "Total à payer" - list_display = ("user", "nb_places", "total", "paid", "paymenttype") - list_filter = ("paid",) - search_fields = ('user__username', 'user__first_name', 'user__last_name') - actions = ['send_attribs',] - actions_on_bottom = True - list_per_page = 400 - - def send_choices(self, request, queryset): - for member in queryset.all(): - choices = member.choixspectacle_set.order_by('priority').all() - if len(choices) == 0: - continue - mail = u"""Cher(e) %s, -Voici tes choix de spectacles tels que notre système les a enregistrés :\n\n""" % member.user.get_full_name() - next_rank = 1 - member_shows = {} - for choice in choices: - if choice.spectacle in member_shows: continue - else: member_shows[choice.spectacle] = True - extra = "" - if choice.double: - extra += u" ; deux places" - if choice.autoquit: - extra += u" ; désistement automatique" - mail += u"- Choix %d : %s%s\n" % (next_rank, choice.spectacle, extra) - next_rank += 1 - mail += u"""\nSi cette liste est incorrecte, merci de nous contacter au plus vite (avant samedi 6 octobre 18h). - -Artistiquement, -Le BdA""" - send_mail ("Choix de spectacles (BdA du COF)", mail, - "bda@ens.fr", [member.user.email], - fail_silently = True) - count = len(queryset.all()) - if count == 1: - message_bit = u"1 membre a" - plural = "" - else: - message_bit = u"%d membres ont" % count - plural = "s" - self.message_user(request, u"%s été informé%s avec succès." % (message_bit, plural)) - send_choices.short_description = u"Envoyer les choix par mail" - - def send_attribs(self, request, queryset): - for member in queryset.all(): - attribs = member.attributions.all() - if len(attribs) == 0: - mail = u"""Cher-e %s, - -Tu t'es inscrit-e pour le troisième tirage au sort du BdA. Nous avons le regret -de t'annoncer que tu n'as pas obtenu de place. - -Nous sommes bien conscients que le nombre de places mises en jeu était -très restreint, mais les premier et deuxième tirages au sort comprenaient -déjà un nombre exceptionnel de places, et nous dépendons des limites -fixées par les théâtres partenaires pour l'obtention de places à tarif réduit. - -Le système de revente des places via les mails BdA-revente reste disponible, -alors surveille tes mails. - -En vous souhaitant une bonne fin de journée, --- -Le Bureau des Arts - -""" - name = member.user.get_full_name() - mail = mail % name - else: - mail = u"""Cher-e %s, - -Tu t'es inscrit-e pour le troisième tirage au sort du BdA. Tu as été -sélectionné-e pour les spectacles suivants : - -%s - -Nous sommes bien conscients que le nombre de places mises en jeu était -très restreint, mais les premier et deuxième tirages au sort comprenaient -déjà un nombre exceptionnel de places, et nous dépendons des limites fixées -par les théâtres partenaires pour l'obtention de places à tarif réduit. - -*Paiement* -L'intégralité de ces places de spectacles est à régler à partir du lundi -11 avril et AVANT le 16 avril, au bureau du COF pendant les -heures de permanences (du lundi au vendredi entre 12h et 14h, et entre 18h -et 20h). Si vous n'êtes pas disponible cette semaine, prévenez-nous par mail. -Attention : au-delà de cette date, les places pourront être remises en jeu. - -*Mode de retrait des places* -Au moment du paiement, les places pour les spectacles de la Comédie-Française -et pour la Philarmonie vous seront remises. - -Nous vous rappelons que l'obtention de places du BdA vous engage à -respecter les règles de fonctionnement : -http://www.cof.ens.fr/bda/?page_id=1370 - -Le système de revente des places via les mails BdA-revente reste disponible, -directement sur votre compte GestioCOF. - -En vous souhaitant de très beaux spectacles, --- -Le Bureau des Arts - -""" - attribs_text = "" - name = member.user.get_full_name() - for attrib in attribs: - attribs_text += u"- 1 place pour %s\n" % attrib - mail = mail % (name, attribs_text) - - send_mail ("[BdA] Résultats du tirage au sort", mail, - "bda@ens.fr", [member.user.email], - fail_silently = True) - count = len(queryset.all()) - if count == 1: - message_bit = u"1 membre a" - plural = "" - else: - message_bit = u"%d membres ont" % count - plural = "s" - self.message_user(request, u"%s été informé%s avec succès." % (message_bit, plural)) - send_attribs.short_description = u"Envoyer les résultats par mail" - -class AttributionAdmin(admin.ModelAdmin): - def paid(self, obj): - return obj.participant.paid - paid.short_description = 'A payé' - paid.boolean = True - list_display = ("id", "spectacle", "participant", "given", "paid") - search_fields = ('spectacle__title', 'participant__user__username', 'participant__user__first_name', 'participant__user__last_name') - -import autocomplete_light -class ChoixSpectacleAdmin(admin.ModelAdmin): - form = autocomplete_light.modelform_factory(ChoixSpectacle, exclude=[]) - list_display = ("participant", "spectacle", "priority", "double", "autoquit") - list_filter = ("double", "autoquit") - search_fields = ('participant__user__username', 'participant__user__first_name', 'participant__user__last_name') - -class SpectacleAdmin(admin.ModelAdmin): - model = Spectacle - list_display = ("title", "date", "location", "slots", "price") - list_filter = ("location",) - search_fields = ("title", "location__name") - -admin.site.register(Spectacle, SpectacleAdmin) -admin.site.register(Salle) -admin.site.register(Participant, ParticipantAdmin) -admin.site.register(Attribution, AttributionAdmin) -admin.site.register(ChoixSpectacle, ChoixSpectacleAdmin) diff --git a/bda3/algorithm.py b/bda3/algorithm.py deleted file mode 100644 index 623f9756..00000000 --- a/bda3/algorithm.py +++ /dev/null @@ -1,104 +0,0 @@ -# coding: utf-8 - -from django.conf import settings -from django.db.models import Max - -import random - -class Algorithm(object): - - shows = None - ranks = None - origranks = None - double = None - - def __init__(self, shows, members, choices): - """Initialisation : - - on aggrège toutes les demandes pour chaque spectacle dans - show.requests - - on crée des tables de demandes pour chaque personne, afin de - pouvoir modifier les rankings""" - self.max_group = 2 * choices.aggregate(Max('priority'))['priority__max'] - self.shows = [] - showdict = {} - for show in shows: - show.nrequests = 0 - showdict[show] = show - show.requests = [] - self.shows.append(show) - self.ranks = {} - self.origranks = {} - self.choices = {} - next_rank = {} - member_shows = {} - for member in members: - self.ranks[member] = {} - self.choices[member] = {} - next_rank[member] = 1 - member_shows[member] = {} - for choice in choices: - member = choice.participant - if choice.spectacle in member_shows[member]: continue - else: member_shows[member][choice.spectacle] = True - showdict[choice.spectacle].requests.append(member) - showdict[choice.spectacle].nrequests += 2 if choice.double else 1 - self.ranks[member][choice.spectacle] = next_rank[member] - next_rank[member] += 2 if choice.double else 1 - self.choices[member][choice.spectacle] = choice - for member in members: - self.origranks[member] = dict(self.ranks[member]) - - def IncrementRanks(self, member, currank, increment = 1): - for show in self.ranks[member]: - if self.ranks[member][show] > currank: - self.ranks[member][show] -= increment - - def appendResult(self, l, member, show): - l.append((member, - self.ranks[member][show], - self.origranks[member][show], - self.choices[member][show].double)) - - """ - Pour les 2 Walkyries: c'est Sandefer - - Pour les 4 places pour l'Oratorio c'est Maxence Arutkin - """ - - def __call__(self, seed): - random.seed(seed) - results = [] - shows = sorted(self.shows, key = lambda x: float(x.nrequests) / x.slots, reverse = True) - for show in shows: - # On regroupe tous les gens ayant le même rang - groups = dict([(i, []) for i in range(1, self.max_group + 1)]) - for member in show.requests: - if self.ranks[member][show] == 0: - raise RuntimeError, (member, show.title) - groups[self.ranks[member][show]].append(member) - # On passe à l'attribution - winners = [] - losers = [] - for i in range(1, self.max_group + 1): - group = list(groups[i]) - random.shuffle(group) - for member in group: - if self.choices[member][show].double: # double - if len(winners) + 1 < show.slots: - self.appendResult(winners, member, show) - self.appendResult(winners, member, show) - elif not self.choices[member][show].autoquit and len(winners) < show.slots: - self.appendResult(winners, member, show) - self.appendResult(losers, member, show) - else: - self.appendResult(losers, member, show) - self.appendResult(losers, member, show) - self.IncrementRanks(member, i, 2) - else: # simple - if len(winners) < show.slots: - self.appendResult(winners, member, show) - else: - self.appendResult(losers, member, show) - self.IncrementRanks(member, i) - results.append((show,winners,losers)) - return results diff --git a/bda3/autocomplete_light_registry.py b/bda3/autocomplete_light_registry.py deleted file mode 100644 index 3254b3c8..00000000 --- a/bda3/autocomplete_light_registry.py +++ /dev/null @@ -1,9 +0,0 @@ -import autocomplete_light - -from bda.models import Participant, Spectacle - -autocomplete_light.register(Participant, search_fields=('user__username','user__first_name','user__last_name'), - autocomplete_js_attributes={'placeholder': 'participant...'}) - -autocomplete_light.register(Spectacle, search_fields=('title',), - autocomplete_js_attributes={'placeholder': 'spectacle...'}) diff --git a/bda3/difftobda b/bda3/difftobda deleted file mode 100644 index 70c81735..00000000 --- a/bda3/difftobda +++ /dev/null @@ -1,421 +0,0 @@ -Only in .: .admin.py.swp -Binary files ../bda/__init__.pyc and ./__init__.pyc differ -diff -p -u -r ../bda/admin.py ./admin.py ---- ../bda/admin.py 2013-12-17 10:19:56.000000000 +0100 -+++ ./admin.py 2012-12-08 22:31:46.000000000 +0100 -@@ -4,8 +4,8 @@ from django.core.mail import send_mail - from django.contrib.contenttypes.models import ContentType - - from django.contrib import admin --from django.db.models import Sum, Count --from bda.models import Spectacle, Salle, Participant, ChoixSpectacle, Attribution -+from django.db.models import Sum -+from bda3.models import Spectacle, Salle, Participant, ChoixSpectacle, Attribution - - class ChoixSpectacleInline(admin.TabularInline): - model = ChoixSpectacle -@@ -17,18 +17,13 @@ class AttributionInline(admin.TabularInl - class ParticipantAdmin(admin.ModelAdmin): - #inlines = [ChoixSpectacleInline] - inlines = [AttributionInline] -- def queryset(self, request): -- return Participant.objects.annotate(nb_places = Count('attributions'), -- total = Sum('attributions__price')) - def nb_places(self, obj): -- return obj.nb_places -- nb_places.admin_order_field = "nb_places" -+ return len(obj.attribution_set.all()) - nb_places.short_description = "Nombre de places" - def total(self, obj): -- tot = obj.total -+ tot = obj.attributions.aggregate(total = Sum('price'))['total'] - if tot: return u"%.02f €" % tot - else: return u"0 €" -- total.admin_order_field = "total" - total.short_description = "Total à payer" - list_display = ("user", "nb_places", "total", "paid", "paymenttype") - list_filter = ("paid",) -@@ -61,7 +56,7 @@ Voici tes choix de spectacles tels que n - Artistiquement, - Le BdA""" - send_mail ("Choix de spectacles (BdA du COF)", mail, -- "bda@ens.fr", [member.user.email], -+ "bda@clipper.ens.fr", [member.user.email], - fail_silently = True) - count = len(queryset.all()) - if count == 1: -@@ -86,48 +81,41 @@ pour les spectacles suivants : - %s - - *Paiement* --L'intégralité de ces places de spectacles est à régler à partir du jeudi --10 octobre et AVANT le mercredi 23 octobre, au bureau du COF pendant les --heures de permanences (du lundi au vendredi entre 12h et 14h, et entre 18h --et 20h). Des facilités de paiement sont bien évidemment possibles : nous --pouvons ne pas encaisser le chèque immédiatement, ou bien découper votre --paiement en deux fois. -+Ces spectacles sont à régler avant le lundi 17 décembre, pendant les -+heures de permanences du COF (tous les jours de la semaine entre 12h et -+14h, et entre 18h et 20h). Des facilités de paiement sont bien évidemment -+possibles (encaissement échelonné des chèques). - - *Mode de retrait des places* --Au moment du paiement, une enveloppe vous sera remise, contenant les --places pour l'Opéra de Paris, pour les premiers spectacles de la Comédie --française, certains spectacles du Châtelet et du Théâtre de la Ville. -- --Pour les concerts Radio France, le Théâtre des Champs-Élysées, le théâtre --du Rond-Point, le théâtre de la Colline, le théâtre de l'Athénée, l'IRCAM, --la Cité de la musique et le 104, le Studio-Théâtre de la Comédie --française, les places seront nominatives et à retirer au théâtre le soir --de la représentation au moins une demi-heure avant le début du spectacle. -- --Pour le théâtre de l'Odéon, la salle Richelieu le théâtre du Vieux --colombier de la Comédie française, certains spectacles du théâtre de la --Ville et du théâtre de Châtelet ainsi que pour le théâtre de Chaillot, les --places seront distribuées environ une semaine avant la représentation (un --mail vous en avertira). -- --Nous vous rappelons que l'obtention de places du BdA vous engage à --respecter les règles de fonctionnement : --http://www.cof.ens.fr/bda/?page_id=1370 --Le système de revente des places via les mails BdA-revente sera très --prochainement disponible, directement sur votre compte GestioCOF. -+Pour l'Opéra de Paris, le théâtre de la Colline et le théâtre du Châtelet, -+les places sont à retirer au COF le jour du paiement. -+ -+Pour les concerts Radio France, le théâtre des Champs-Élysées, l'IRCAM -+et le 104, les places seront nominatives et à retirer à la salle le soir de -+la représentation au moins une demi-heure avant le début du spectacle. -+ -+Pour le théâtre de l'Odéon, la Comédie Française, le théâtre de la Ville, -+le théâtre de Chaillot, les places seront distribuées dans vos -+casiers environ une semaine avant la représentation (un mail vous en -+avertira). -+ -+Dans tous les cas, n'hésitez pas à nous contacter si vous avez un doute ! -+ -+Nous profitons aussi de ce message pour vous annoncer qu'un festival de -+courts métrages aura lieu le 21 décembre dans l'école. -+Plus d'informations : http://www.cof.ens.fr/bda/courts. -+ -+Culturellement vôtre, - --En vous souhaitant de très beaux spectacles tout au long de l'année, - -- --Le Bureau des Arts --(Chloé, Emilie, Jaime, Maxime, Olivier) --""" -+Le BdA""" - attribs_text = "" - name = member.user.get_full_name() - for attrib in attribs: - attribs_text += u"- 1 place pour %s\n" % attrib - mail = mail % (name, attribs_text) -- send_mail ("Résultats du tirage au sort", mail, -- "bda@ens.fr", [member.user.email], -+ send_mail ("Places de spectacle (BdA du COF)", mail, -+ "bda@clipper.ens.fr", [member.user.email], - fail_silently = True) - count = len(queryset.all()) - if count == 1: -@@ -154,13 +142,7 @@ class ChoixSpectacleAdmin(admin.ModelAdm - list_filter = ("double", "autoquit") - search_fields = ('participant__user__username', 'participant__user__first_name', 'participant__user__last_name') - --class SpectacleAdmin(admin.ModelAdmin): -- model = Spectacle -- list_display = ("title", "date", "location", "slots", "price") -- list_filter = ("location",) -- search_fields = ("title", "location__name") -- --admin.site.register(Spectacle, SpectacleAdmin) -+admin.site.register(Spectacle) - admin.site.register(Salle) - admin.site.register(Participant, ParticipantAdmin) - admin.site.register(Attribution, AttributionAdmin) -diff -p -u -r ../bda/algorithm.py ./algorithm.py ---- ../bda/algorithm.py 2013-10-07 14:08:44.000000000 +0200 -+++ ./algorithm.py 2012-10-31 23:01:48.000000000 +0100 -@@ -29,24 +29,25 @@ class Algorithm(object): - self.ranks = {} - self.origranks = {} - self.choices = {} -- next_rank = {} -- member_shows = {} - for member in members: -- self.ranks[member] = {} -- self.choices[member] = {} -- next_rank[member] = 1 -- member_shows[member] = {} -- for choice in choices: -- member = choice.participant -- if choice.spectacle in member_shows[member]: continue -- else: member_shows[member][choice.spectacle] = True -- showdict[choice.spectacle].requests.append(member) -- showdict[choice.spectacle].nrequests += 2 if choice.double else 1 -- self.ranks[member][choice.spectacle] = next_rank[member] -- next_rank[member] += 2 if choice.double else 1 -- self.choices[member][choice.spectacle] = choice -- for member in members: -- self.origranks[member] = dict(self.ranks[member]) -+ ranks = {} -+ member_choices = {} -+ member_shows = {} -+ #next_priority = 1 -+ next_rank = 1 -+ for choice in member.choixspectacle_set.order_by('priority').all(): -+ if choice.spectacle in member_shows: continue -+ else: member_shows[choice.spectacle] = True -+ #assert choice.priority == next_priority -+ #next_priority += 1 -+ showdict[choice.spectacle].requests.append(member) -+ showdict[choice.spectacle].nrequests += 2 if choice.double else 1 -+ ranks[choice.spectacle] = next_rank -+ next_rank += 2 if choice.double else 1 -+ member_choices[choice.spectacle] = choice -+ self.ranks[member] = ranks -+ self.choices[member] = member_choices -+ self.origranks[member] = dict(ranks) - - def IncrementRanks(self, member, currank, increment = 1): - for show in self.ranks[member]: -Only in ../bda: algorithm.pyc -Only in .: difftobda -diff -p -u -r ../bda/models.py ./models.py ---- ../bda/models.py 2013-10-10 10:44:43.000000000 +0200 -+++ ./models.py 2012-10-31 23:12:56.000000000 +0100 -@@ -1,7 +1,5 @@ - # coding: utf-8 - --import calendar -- - from django.db import models - from django.contrib.auth.models import User - from django.utils.translation import ugettext_lazy as _ -@@ -19,7 +17,7 @@ class Spectacle (models.Model): - date = models.DateTimeField ("Date & heure") - location = models.ForeignKey(Salle) - description = models.TextField ("Description", blank = True) -- slots_description = models.TextField ("Description des places", blank = True) -+ #slots_description = models.TextField ("Description des places", blank = True) - price = models.FloatField("Prix d'une place", blank = True) - slots = models.IntegerField ("Places") - priority = models.IntegerField ("Priorité", default = 1000) -@@ -31,14 +29,11 @@ class Spectacle (models.Model): - def __repr__ (self): - return u"[%s]" % self.__unicode__() - -- def timestamp(self): -- return "%d" % calendar.timegm(self.date.utctimetuple()) -- - def date_no_seconds(self): - return self.date.strftime('%d %b %Y %H:%M') - - def __unicode__ (self): -- return u"%s - %s, %s, %.02f€" % (self.title, self.date_no_seconds(), self.location, self.price) -+ return u"%s - %s @ %s, %.02f€" % (self.title, self.date_no_seconds(), self.location, self.price) - - PAYMENT_TYPES = ( - ("cash",u"Cash"), -@@ -48,7 +43,7 @@ PAYMENT_TYPES = ( - ) - - class Participant (models.Model): -- user = models.ForeignKey(User, unique = True) -+ user = models.ForeignKey(User, unique = True, related_name = "participants2") - choices = models.ManyToManyField(Spectacle, through = "ChoixSpectacle", related_name = "chosen_by") - attributions = models.ManyToManyField(Spectacle, through = "Attribution", related_name = "attributed_to") - paid = models.BooleanField (u"A payé", default = False) -Binary files ../bda/models.pyc and ./models.pyc differ -diff -p -u -r ../bda/views.py ./views.py ---- ../bda/views.py 2014-01-04 21:37:15.000000000 +0100 -+++ ./views.py 2013-02-12 22:03:38.000000000 +0100 -@@ -1,23 +1,19 @@ - # coding: utf-8 - --from django.contrib.auth.models import User - from django.shortcuts import render, get_object_or_404 - from django.contrib.auth.decorators import login_required - from django.db import models - from django.http import Http404 - from django import forms - from django.forms.models import inlineformset_factory, BaseInlineFormSet --from django.core import serializers --import hashlib - - from django.core.mail import send_mail - --from datetime import datetime - import time - - from gestioncof.decorators import cof_required, buro_required --from bda.models import Spectacle, Participant, ChoixSpectacle, Attribution --from bda.algorithm import Algorithm -+from bda3.models import Spectacle, Participant, ChoixSpectacle, Attribution -+from bda3.algorithm import Algorithm - - class BaseBdaFormSet(BaseInlineFormSet): - def clean(self): -@@ -37,7 +33,7 @@ class BaseBdaFormSet(BaseInlineFormSet): - raise forms.ValidationError("Vous ne pouvez pas vous inscrire deux fois pour le même spectacle.") - spectacles.append(spectacle) - --@cof_required -+@buro_required - def etat_places(request): - spectacles1 = ChoixSpectacle.objects.all().values('spectacle','spectacle__title').annotate(total = models.Count('spectacle')) - spectacles2 = ChoixSpectacle.objects.filter(double = True).all().values('spectacle','spectacle__title').annotate(total = models.Count('spectacle')) -@@ -46,93 +42,47 @@ def etat_places(request): - total = 0 - for spectacle in spectacles: - spectacle.total = 0 -- spectacle.ratio = -1.0 - spectacles_dict[spectacle.id] = spectacle - for spectacle in spectacles1: - spectacles_dict[spectacle["spectacle"]].total += spectacle["total"] -- spectacles_dict[spectacle["spectacle"]].ratio = spectacles_dict[spectacle["spectacle"]].total/float(spectacles_dict[spectacle["spectacle"]].slots) - total += spectacle["total"] - for spectacle in spectacles2: - spectacles_dict[spectacle["spectacle"]].total += spectacle["total"] -- spectacles_dict[spectacle["spectacle"]].ratio = spectacles_dict[spectacle["spectacle"]].total/float(spectacles_dict[spectacle["spectacle"]].slots) - total += spectacle["total"] - return render(request, "etat-places.html", {"spectacles": spectacles, "total": total}) - --def _hash_queryset(queryset): -- data = serializers.serialize("json", queryset) -- hasher = hashlib.sha256() -- hasher.update(data) -- return hasher.hexdigest() -- --@cof_required --def places(request): -- participant, created = Participant.objects.get_or_create(user = request.user) -- places = participant.attribution_set.order_by("spectacle__date", "spectacle").all() -- total = sum([place.spectacle.price for place in places]) -- filtered_places = [] -- places_dict = {} -- spectacles = [] -- dates = [] -- warning = False -- for place in places: -- if place.spectacle in spectacles: -- places_dict[place.spectacle].double = True -- else: -- place.double = False -- places_dict[place.spectacle] = place -- spectacles.append(place.spectacle) -- filtered_places.append(place) -- date = place.spectacle.date.date() -- if date in dates: -- warning = True -- else: -- dates.append(date) -- return render(request, "resume_places.html", -- {"participant": participant, -- "places": filtered_places, -- "total": total, -- "warning": warning}) -- - @cof_required - def inscription(request): -- if datetime.now() > datetime(2013, 10, 6, 23, 59): -- participant, created = Participant.objects.get_or_create(user = request.user) -- choices = participant.choixspectacle_set.order_by("priority").all() -- return render(request, "resume_inscription.html", {"error_title": "C'est fini !", "error_description": u"Tirage au sort le 7 octobre !", "choices": choices}) -+ if time.time() > 1354921200: -+ return render(request, "error.html", {"error_title": "C'est fini !", "error_description": u"Tirage au sort le 6 octobre dans la soirée "}) - BdaFormSet = inlineformset_factory(Participant, ChoixSpectacle, fields = ("spectacle","double","autoquit","priority",), formset = BaseBdaFormSet) - participant, created = Participant.objects.get_or_create(user = request.user) - success = False -- stateerror = False - if request.method == "POST": -- dbstate = _hash_queryset(participant.choixspectacle_set.all()) -- if "dbstate" in request.POST and dbstate != request.POST["dbstate"]: -- stateerror = True -+ formset = BdaFormSet(request.POST, instance = participant) -+ if formset.is_valid(): -+ #ChoixSpectacle.objects.filter(participant = participant).delete() -+ formset.save() -+ success = True - formset = BdaFormSet(instance = participant) -- else: -- formset = BdaFormSet(request.POST, instance = participant) -- if formset.is_valid(): -- #ChoixSpectacle.objects.filter(participant = participant).delete() -- formset.save() -- success = True -- formset = BdaFormSet(instance = participant) - else: - formset = BdaFormSet(instance = participant) -- dbstate = _hash_queryset(participant.choixspectacle_set.all()) - total_price = 0 - for choice in participant.choixspectacle_set.all(): - total_price += choice.spectacle.price - if choice.double: total_price += choice.spectacle.price -- return render(request, "inscription-bda.html", {"formset": formset, "success": success, "total_price": total_price, "dbstate": dbstate, "stateerror": stateerror}) -+ return render(request, "inscription-bda.html", {"formset": formset, "success": success, "total_price": total_price}) -+ -+Spectacle.deficit = lambda x: (x.slots-x.nrequests)*x.price - - def do_tirage(request): - form = TokenForm(request.POST) - if not form.is_valid(): - return tirage(request) -- start = time.time() - data = {} -- shows = Spectacle.objects.select_related().all() -+ shows = Spectacle.objects.all() - members = Participant.objects.all() -- choices = ChoixSpectacle.objects.order_by('participant', 'priority').select_related().all() -+ choices = ChoixSpectacle.objects.all() - algo = Algorithm(shows, members, choices) - results = algo(form.cleaned_data["token"]) - total_slots = 0 -@@ -149,8 +99,8 @@ def do_tirage(request): - total_sold = 0 - total_deficit = 0 - opera_deficit = 0 -- for (show, members, _) in results: -- deficit = (show.slots - len(members)) * show.price -+ for show in shows: -+ deficit = show.deficit() - total_sold += show.slots * show.price - if deficit >= 0: - if u"Opéra" in show.location.name: -@@ -159,23 +109,18 @@ def do_tirage(request): - data["total_sold"] = total_sold - total_deficit - data["total_deficit"] = total_deficit - data["opera_deficit"] = opera_deficit -- data["duration"] = time.time() - start - if request.user.is_authenticated(): - members2 = {} -- members_uniq = {} # Participant objects are not shared accross spectacle results, -- # So assign a single object for each Participant id - for (show, members, _) in results: - for (member, _, _, _) in members: -- if member.id not in members_uniq: -- members_uniq[member.id] = member -+ if member not in members2: - members2[member] = [] - member.total = 0 -- member = members_uniq[member.id] - members2[member].append(show) - member.total += show.price - members2 = members2.items() - data["members2"] = sorted(members2, key = lambda m: m[0].user.last_name) -- if False and request.user.username in ["seguin", "harazi"]: -+ if False and request.user.username == "seguin": - Attribution.objects.all().delete() - for (show, members, _) in results: - for (member, _, _, _) in members: -@@ -220,7 +165,7 @@ Je souhaite revendre %s pour %s le %s (% - Contactez moi par email si vous êtes intéressés ! - - %s (%s)""" % (places, spectacle.title, spectacle.date_no_seconds(), spectacle.location, spectacle.price, request.user.get_full_name(), request.user.email) -- send_mail("%s" % spectacle, mail, -+ send_mail("Revente de place: %s" % spectacle, mail, - request.user.email, ["bda-revente@lists.ens.fr"], - fail_silently = True) - return render(request, "bda-success.html", {"show": spectacle, "places": places}) -Only in .: views.py~ diff --git a/bda3/forms.py b/bda3/forms.py deleted file mode 100644 index 616c96b1..00000000 --- a/bda3/forms.py +++ /dev/null @@ -1,39 +0,0 @@ -# coding: utf-8 - -from django import forms -from django.forms.models import inlineformset_factory, BaseInlineFormSet -from bda3.models import Spectacle, Participant, ChoixSpectacle, Attribution - -class BaseBdaFormSet(BaseInlineFormSet): - def clean(self): - """Checks that no two articles have the same title.""" - super(BaseBdaFormSet, self).clean() - if any(self.errors): - # Don't bother validating the formset unless each form is valid on its own - return - spectacles = [] - for i in range(0, self.total_form_count()): - form = self.forms[i] - if not form.cleaned_data: - continue - spectacle = form.cleaned_data['spectacle'] - delete = form.cleaned_data['DELETE'] - if not delete and spectacle in spectacles: - raise forms.ValidationError("Vous ne pouvez pas vous inscrire deux fois pour le même spectacle.") - spectacles.append(spectacle) - -class TokenForm(forms.Form): - token = forms.CharField(widget = forms.widgets.Textarea()) - -class SpectacleModelChoiceField(forms.ModelChoiceField): - def label_from_instance(self, obj): - return u"%s le %s (%s) à %.02f€" % (obj.title, obj.date_no_seconds(), obj.location, obj.price) - -class ResellForm(forms.Form): - count = forms.ChoiceField(choices = (("1","1"),("2","2"),)) - spectacle = SpectacleModelChoiceField(queryset = Spectacle.objects.none()) - - def __init__(self, participant, *args, **kwargs): - super(ResellForm, self).__init__(*args, **kwargs) - self.fields['spectacle'].queryset = participant.attributions.all().distinct() - diff --git a/bda3/migrations/0001_initial.py b/bda3/migrations/0001_initial.py deleted file mode 100644 index 7a7e061b..00000000 --- a/bda3/migrations/0001_initial.py +++ /dev/null @@ -1,109 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models -from django.conf import settings - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='Attribution', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('given', models.BooleanField(default=False, verbose_name='Donn\xe9e')), - ], - ), - migrations.CreateModel( - name='ChoixSpectacle', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('priority', models.PositiveIntegerField(verbose_name=b'Priorit\xc3\xa9')), - ('double', models.BooleanField(default=False, verbose_name=b'Deux places1')), - ('autoquit', models.BooleanField(default=False, verbose_name=b'Abandon2')), - ], - options={ - 'ordering': ('priority',), - 'verbose_name': 'voeu', - 'verbose_name_plural': 'voeux', - }, - ), - migrations.CreateModel( - name='Participant', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('paid', models.BooleanField(default=False, verbose_name='A pay\xe9')), - ('paymenttype', models.CharField(blank=True, max_length=6, verbose_name='Moyen de paiement', choices=[(b'cash', 'Cash'), (b'cb', b'CB'), (b'cheque', 'Ch\xe8que'), (b'autre', 'Autre')])), - ], - ), - migrations.CreateModel( - name='Salle', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField(max_length=300, verbose_name=b'Nom')), - ('address', models.TextField(verbose_name=b'Adresse')), - ], - ), - migrations.CreateModel( - name='Spectacle', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('title', models.CharField(max_length=300, verbose_name=b'Titre')), - ('date', models.DateTimeField(verbose_name=b'Date & heure')), - ('description', models.TextField(verbose_name=b'Description', blank=True)), - ('slots_description', models.TextField(verbose_name=b'Description des places', blank=True)), - ('price', models.FloatField(verbose_name=b"Prix d'une place", blank=True)), - ('slots', models.IntegerField(verbose_name=b'Places')), - ('priority', models.IntegerField(default=1000, verbose_name=b'Priorit\xc3\xa9')), - ('location', models.ForeignKey(to='bda3.Salle')), - ], - options={ - 'ordering': ('priority', 'date', 'title'), - 'verbose_name': 'Spectacle', - }, - ), - migrations.AddField( - model_name='participant', - name='attributions', - field=models.ManyToManyField(related_name='attributed_to', through='bda3.Attribution', to='bda3.Spectacle'), - ), - migrations.AddField( - model_name='participant', - name='choices', - field=models.ManyToManyField(related_name='chosen_by', through='bda3.ChoixSpectacle', to='bda3.Spectacle'), - ), - migrations.AddField( - model_name='participant', - name='user', - field=models.OneToOneField(related_name='participants3', to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='choixspectacle', - name='participant', - field=models.ForeignKey(to='bda3.Participant'), - ), - migrations.AddField( - model_name='choixspectacle', - name='spectacle', - field=models.ForeignKey(related_name='participants', to='bda3.Spectacle'), - ), - migrations.AddField( - model_name='attribution', - name='participant', - field=models.ForeignKey(to='bda3.Participant'), - ), - migrations.AddField( - model_name='attribution', - name='spectacle', - field=models.ForeignKey(related_name='attribues', to='bda3.Spectacle'), - ), - migrations.AlterUniqueTogether( - name='choixspectacle', - unique_together=set([('participant', 'spectacle')]), - ), - ] diff --git a/bda3/migrations/__init__.py b/bda3/migrations/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/bda3/models.py b/bda3/models.py deleted file mode 100644 index 6ada1be3..00000000 --- a/bda3/models.py +++ /dev/null @@ -1,78 +0,0 @@ -# coding: utf-8 - -import calendar - -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 - -class Salle (models.Model): - name = models.CharField ("Nom", max_length = 300) - address = models.TextField ("Adresse") - - def __unicode__ (self): - return self.name - -class Spectacle (models.Model): - title = models.CharField ("Titre", max_length = 300) - date = models.DateTimeField ("Date & heure") - location = models.ForeignKey(Salle) - description = models.TextField ("Description", blank = True) - slots_description = models.TextField ("Description des places", blank = True) - price = models.FloatField("Prix d'une place", blank = True) - slots = models.IntegerField ("Places") - priority = models.IntegerField ("Priorité", default = 1000) - - class Meta: - verbose_name = "Spectacle" - ordering = ("priority", "date","title",) - - def __repr__ (self): - return u"[%s]" % self.__unicode__() - - def timestamp(self): - return "%d" % calendar.timegm(self.date.utctimetuple()) - - def date_no_seconds(self): - return self.date.strftime('%d %b %Y %H:%M') - - def __unicode__ (self): - return u"%s - %s, %s, %.02f€" % (self.title, self.date_no_seconds(), self.location, self.price) - -PAYMENT_TYPES = ( -("cash",u"Cash"), -("cb","CB"), -("cheque",u"Chèque"), -("autre",u"Autre"), -) - -class Participant (models.Model): - user = models.OneToOneField(User, related_name = "participants3") - choices = models.ManyToManyField(Spectacle, through = "ChoixSpectacle", related_name = "chosen_by") - attributions = models.ManyToManyField(Spectacle, through = "Attribution", related_name = "attributed_to") - paid = models.BooleanField (u"A payé", default = False) - paymenttype = models.CharField(u"Moyen de paiement", max_length = 6, choices = PAYMENT_TYPES, blank = True) - - def __unicode__ (self): - return u"%s" % (self.user) - -class ChoixSpectacle (models.Model): - participant = models.ForeignKey(Participant) - spectacle = models.ForeignKey(Spectacle, related_name = "participants") - priority = models.PositiveIntegerField("Priorité") - double = models.BooleanField("Deux places1",default=False) - autoquit = models.BooleanField("Abandon2",default=False) - class Meta: - ordering = ("priority",) - unique_together = (("participant", "spectacle",),) - verbose_name = "voeu" - verbose_name_plural = "voeux" - -class Attribution (models.Model): - participant = models.ForeignKey(Participant) - spectacle = models.ForeignKey(Spectacle, related_name = "attribues") - given = models.BooleanField(u"Donnée", default = False) - - def __unicode__ (self): - return u"%s -- %s" % (self.participant, self.spectacle) diff --git a/bda3/static/css/bda.css b/bda3/static/css/bda.css deleted file mode 100644 index 8851eeee..00000000 --- a/bda3/static/css/bda.css +++ /dev/null @@ -1,10 +0,0 @@ -form#tokenform {text-align: center; font-size: 2em;} -label {margin-right: 10px; vertical-align: top;} -form#tokenform textarea {font-size: 2em; width: 350px; height: 200px; font-family: 'Droif Serif', serif;} -input {width: 400px; font-size: 2em;} -ul.losers {display: inline; margin: 0; padding: 0;} -ul.losers li {display: inline;} -span.details {font-size: 0.7em;} -table {border: 1px solid black; border-collapse: collapse;} -td {border: 1px solid black; padding: 2px;} -.attribresult {margin: 10px 0px;} diff --git a/bda3/templates/spectacle_list.html b/bda3/templates/spectacle_list.html deleted file mode 100644 index a4a67998..00000000 --- a/bda3/templates/spectacle_list.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends "base_title.html" %} - -{% block realcontent %} -

Spectacles

- -{% endblock %} diff --git a/bda3/tests.py b/bda3/tests.py deleted file mode 100644 index 501deb77..00000000 --- a/bda3/tests.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -This file demonstrates writing tests using the unittest module. These will pass -when you run "manage.py test". - -Replace this with more appropriate tests for your application. -""" - -from django.test import TestCase - - -class SimpleTest(TestCase): - def test_basic_addition(self): - """ - Tests that 1 + 1 always equals 2. - """ - self.assertEqual(1 + 1, 2) diff --git a/bda3/views.py b/bda3/views.py deleted file mode 100644 index 5c237b5d..00000000 --- a/bda3/views.py +++ /dev/null @@ -1,215 +0,0 @@ - -# coding: utf-8 - -from django.contrib.auth.models import User -from django.shortcuts import render, get_object_or_404 -from django.contrib.auth.decorators import login_required -from django.db import models -from django.http import Http404 -from django.core import serializers -import hashlib - -from django.core.mail import send_mail - -from datetime import datetime -import time - -from gestioncof.decorators import cof_required, buro_required -from bda3.models import Spectacle, Participant, ChoixSpectacle, Attribution -from bda3.algorithm import Algorithm -from bda3.forms import BaseBdaFormSet, TokenForm, ResellForm - -@cof_required -def etat_places(request): - spectacles1 = ChoixSpectacle.objects.all().values('spectacle','spectacle__title').annotate(total = models.Count('spectacle')) - spectacles2 = ChoixSpectacle.objects.filter(double = True).all().values('spectacle','spectacle__title').annotate(total = models.Count('spectacle')) - spectacles = Spectacle.objects.all() - spectacles_dict = {} - total = 0 - for spectacle in spectacles: - spectacle.total = 0 - spectacle.ratio = -1.0 - spectacles_dict[spectacle.id] = spectacle - for spectacle in spectacles1: - spectacles_dict[spectacle["spectacle"]].total += spectacle["total"] - spectacles_dict[spectacle["spectacle"]].ratio = spectacles_dict[spectacle["spectacle"]].total/float(spectacles_dict[spectacle["spectacle"]].slots) - total += spectacle["total"] - for spectacle in spectacles2: - spectacles_dict[spectacle["spectacle"]].total += spectacle["total"] - spectacles_dict[spectacle["spectacle"]].ratio = spectacles_dict[spectacle["spectacle"]].total/float(spectacles_dict[spectacle["spectacle"]].slots) - total += spectacle["total"] - return render(request, "etat-places.html", {"spectacles": spectacles, "total": total}) - -def _hash_queryset(queryset): - data = serializers.serialize("json", queryset) - hasher = hashlib.sha256() - hasher.update(data) - return hasher.hexdigest() - -@cof_required -def places(request): - participant, created = Participant.objects.get_or_create(user = request.user) - places = participant.attribution_set.order_by("spectacle__date", "spectacle").all() - total = sum([place.spectacle.price for place in places]) - filtered_places = [] - places_dict = {} - spectacles = [] - dates = [] - warning = False - for place in places: - if place.spectacle in spectacles: - places_dict[place.spectacle].double = True - else: - place.double = False - places_dict[place.spectacle] = place - spectacles.append(place.spectacle) - filtered_places.append(place) - date = place.spectacle.date.date() - if date in dates: - warning = True - else: - dates.append(date) - return render(request, "resume_places.html", - {"participant": participant, - "places": filtered_places, - "total": total, - "warning": warning}) - -@cof_required -def inscription(request): - if datetime.now() > datetime(2016, 4, 10, 11, 59): - participant, created = Participant.objects.get_or_create(user = request.user) - choices = participant.choixspectacle_set.order_by("priority").all() - return render(request, "resume_inscription.html", {"error_title": "C'est fini !", "error_description": u"Tirage au sort très bientôt !", "choices": choices}) - BdaFormSet = inlineformset_factory(Participant, ChoixSpectacle, fields = ("spectacle","double","autoquit","priority",), formset = BaseBdaFormSet) - participant, created = Participant.objects.get_or_create(user = request.user) - success = False - stateerror = False - if request.method == "POST": - dbstate = _hash_queryset(participant.choixspectacle_set.all()) - if "dbstate" in request.POST and dbstate != request.POST["dbstate"]: - stateerror = True - formset = BdaFormSet(instance = participant) - else: - formset = BdaFormSet(request.POST, instance = participant) - if formset.is_valid(): - #ChoixSpectacle.objects.filter(participant = participant).delete() - formset.save() - success = True - formset = BdaFormSet(instance = participant) - else: - formset = BdaFormSet(instance = participant) - dbstate = _hash_queryset(participant.choixspectacle_set.all()) - total_price = 0 - for choice in participant.choixspectacle_set.all(): - total_price += choice.spectacle.price - if choice.double: total_price += choice.spectacle.price - return render(request, "inscription-bda3.html", {"formset": formset, "success": success, "total_price": total_price, "dbstate": dbstate, "stateerror": stateerror}) - -def do_tirage(request): - form = TokenForm(request.POST) - if not form.is_valid(): - return tirage(request) - start = time.time() - data = {} - shows = Spectacle.objects.select_related().all() - members = Participant.objects.all() - choices = ChoixSpectacle.objects.order_by('participant', 'priority').select_related().all() - algo = Algorithm(shows, members, choices) - results = algo(form.cleaned_data["token"]) - total_slots = 0 - total_losers = 0 - for (_, members, losers) in results: - total_slots += len(members) - total_losers += len(losers) - data["total_slots"] = total_slots - data["total_losers"] = total_losers - data["shows"] = shows - data["token"] = form.cleaned_data["token"] - data["members"] = members - data["results"] = results - total_sold = 0 - total_deficit = 0 - opera_deficit = 0 - for (show, members, _) in results: - deficit = (show.slots - len(members)) * show.price - total_sold += show.slots * show.price - if deficit >= 0: - if u"Opéra" in show.location.name: - opera_deficit += deficit - total_deficit += deficit - data["total_sold"] = total_sold - total_deficit - data["total_deficit"] = total_deficit - data["opera_deficit"] = opera_deficit - data["duration"] = time.time() - start - if request.user.is_authenticated(): - members2 = {} - members_uniq = {} # Participant objects are not shared accross spectacle results, - # So assign a single object for each Participant id - 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 - members2 = members2.items() - data["members2"] = sorted(members2, key = lambda m: m[0].user.last_name) - if False and request.user.username in ["seguin", "harazi", "fromherz", "mpepin"]: - Attribution.objects.all().delete() - for (show, members, _) in results: - for (member, _, _, _) in members: - attrib = Attribution(spectacle = show, participant = member) - attrib.save() - return render(request, "bda-attrib-extra.html", data) - else: - return render(request, "bda-attrib.html", data) - -@login_required -def tirage(request): - if request.POST: - form = TokenForm(request.POST) - if form.is_valid(): - return do_tirage(request) - else: - form = TokenForm() - return render(request, "bda-token.html", {"form": form}) - -def do_resell(request, form): - spectacle = form.cleaned_data["spectacle"] - count = form.cleaned_data["count"] - places = "2 places" if count == "2" else "une place" - mail = u"""Bonjour, - -Je souhaite revendre %s pour %s le %s (%s) à %.02f€. -Contactez moi par email si vous êtes intéressés ! - -%s (%s)""" % (places, spectacle.title, spectacle.date_no_seconds(), spectacle.location, spectacle.price, request.user.get_full_name(), request.user.email) - send_mail("%s" % spectacle, mail, - request.user.email, ["bda-revente@lists.ens.fr"], - fail_silently = True) - return render(request, "bda-success.html", {"show": spectacle, "places": places}) - -@login_required -def revente(request): - participant, created = Participant.objects.get_or_create(user = request.user) - if not participant.paid: - return render(request, "bda-notpaid.html", {}) - if request.POST: - form = ResellForm(participant, request.POST) - if form.is_valid(): - return do_resell(request, form) - else: - form = ResellForm(participant) - return render(request, "bda-revente.html", {"form": form}) - -@buro_required -def spectacle(request, spectacle_id): - spectacle = get_object_or_404(Spectacle, id = spectacle_id) - return render(request, "bda-emails.html", {"spectacle": spectacle}) - -@buro_required -def unpaid(request): - return render(request, "bda-unpaid.html", {"unpaid": Participant.objects.filter(paid = False).all()}) diff --git a/cof/settings_dev.py b/cof/settings_dev.py index 3246b6e5..401127ef 100644 --- a/cof/settings_dev.py +++ b/cof/settings_dev.py @@ -41,8 +41,6 @@ INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.admindocs', 'bda', - 'bda2', - 'bda3', 'autocomplete_light', 'captcha', 'django_cas_ng', diff --git a/cof/urls.py b/cof/urls.py index 3e63ae02..e841ee02 100644 --- a/cof/urls.py +++ b/cof/urls.py @@ -6,11 +6,9 @@ autocomplete_light.autodiscover() from django.contrib import admin admin.autodiscover() -from django.views.generic.list import ListView from django.views.generic.base import TemplateView from bda.models import Spectacle -from bda2.models import Spectacle as Spectacle2 -from bda3.models import Spectacle as Spectacle3 +from bda.views import SpectacleListView from gestioncof.petits_cours_views import DemandeListView urlpatterns = patterns('', @@ -42,32 +40,16 @@ urlpatterns = patterns('', url(r'^petitcours/demandes/(?P\d+)$', 'gestioncof.petits_cours_views.details', name = 'petits-cours-demande-details'), url(r'^petitcours/demandes/(?P\d+)/traitement$', 'gestioncof.petits_cours_views.traitement', name = 'petits-cours-demande-traitement'), url(r'^petitcours/demandes/(?P\d+)/retraitement$', 'gestioncof.petits_cours_views.retraitement', name = 'petits-cours-demande-retraitement'), - url(r'^bda/inscription$', 'bda.views.inscription', name = 'bda-tirage-inscription'), - url(r'^bda2/inscription$', 'bda2.views.inscription', name = 'bda2-tirage-inscription'), - url(r'^bda3/inscription$', 'bda3.views.inscription', name = 'bda3-tirage-inscription'), - url(r'^bda/places$', 'bda.views.places', name = "bda-places-attribuees"), - url(r'^bda/places/places_bda.ics$', 'bda.views.places_ics', name = "bda-places-attribuees-ics"), - url(r'^bda2/places$', 'bda2.views.places', name = "bda2-places-attribuees"), - url(r'^bda3/places$', 'bda3.views.places', name = "bda3-places-attribuees"), - url(r'^bda/revente$', 'bda.views.revente', name = 'bda-revente'), - url(r'^bda2/revente$', 'bda2.views.revente', name = 'bda2-revente'), - url(r'^bda3/revente$', 'bda3.views.revente', name = 'bda3-revente'), - url(r'^bda/etat-places$', 'bda.views.etat_places', name = 'bda-etat-places'), - url(r'^bda2/etat-places$', 'bda2.views.etat_places', name = 'bda2-etat-places'), - url(r'^bda3/etat-places$', 'bda3.views.etat_places', name = 'bda3-etat-places'), - url(r'^bda/tirage$', 'bda.views.tirage'), - url(r'^bda2/tirage$', 'bda2.views.tirage'), - url(r'^bda3/tirage$', 'bda3.views.tirage'), - url(r'^bda/spectacles/$', ListView.as_view(model = Spectacle), name ="bda-liste-spectacles"), - url(r'^bda/spectacles/liste_spectacles.ics$', 'bda.views.liste_spectacles_ics', name ="bda-liste-spectacles-ics"), - url(r'^bda/spectacles/unpaid$', "bda.views.unpaid", name = "bda-unpaid"), - url(r'^bda/spectacles/(?P\d+)$', "bda.views.spectacle", name = "bda-spectacle"), - url(r'^bda/spectacles-2/$', ListView.as_view(model = Spectacle2), name ="bda2-liste-spectacles"), - url(r'^bda/spectacles-2/unpaid$', "bda2.views.unpaid", name = "bda2-unpaid"), - url(r'^bda/spectacles-2/(?P\d+)$', "bda2.views.spectacle", name = "bda2-spectacle"), - url(r'^bda/spectacles-3/$', ListView.as_view(model = Spectacle3), name ="bda3-liste-spectacles"), - url(r'^bda/spectacles-3/unpaid$', "bda3.views.unpaid", name = "bda3-unpaid"), - url(r'^bda/spectacles-3/(?P\d+)$', "bda3.views.spectacle", name = "bda3-spectacle"), + url(r'^bda/inscription/(?P\d+)$', 'bda.views.inscription', name = 'bda-tirage-inscription'), + url(r'^bda/places/(?P\d+)$', 'bda.views.places', name = "bda-places-attribuees"), + url(r'^bda/places/(?P\d+)/places_bda.ics$', 'bda.views.places_ics', name = "bda-places-attribuees-ics"), + url(r'^bda/revente/(?P\d+)$', 'bda.views.revente', name = 'bda-revente'), + url(r'^bda/etat-places/(?P\d+)$', 'bda.views.etat_places', name = 'bda-etat-places'), + url(r'^bda/tirage/(?P\d+)$', 'bda.views.tirage'), + url(r'^bda/spectacles/(?P\d+)$', SpectacleListView.as_view() , name ="bda-liste-spectacles"), + 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'^survey/(?P\d+)$', 'gestioncof.views.survey'), url(r'^event/(?P\d+)$', 'gestioncof.views.event'), url(r'^survey/(?P\d+)/status$', 'gestioncof.views.survey_status'), diff --git a/gestioncof/templates/home.html b/gestioncof/templates/home.html index c47a32e0..f0aecebc 100644 --- a/gestioncof/templates/home.html +++ b/gestioncof/templates/home.html @@ -21,62 +21,52 @@ {% endfor %} {% endif %} - - - {% if user.profile.is_cof %} -

BdA

- - {% endif %} - + + {% if user.profile.is_cof %} + {% if open_tirages %} +

Tirages du BdA

+ {% for tirage in open_tirages %} + + {% endfor %} + {% endif %} + {% endif %} +

Divers

{% if user.profile.is_buro %}

Administration

{% endif %} -

Pour tout problème : cof@ens.fr.

+

Pour tout problème : cof@ens.fr.

{% endblock %} diff --git a/gestioncof/templates/utile_bda.html b/gestioncof/templates/utile_bda.html index d2195fa7..5f4f383a 100644 --- a/gestioncof/templates/utile_bda.html +++ b/gestioncof/templates/utile_bda.html @@ -10,23 +10,14 @@
  • BdA diffusion
  • BdA revente
  • -

    Premier tirage

    +

    Tirages

    + {% for tirage in tirages %} +

    {{ tirage.title }}

    -

    Deuxième tirage

    - -

    Troisième tirage

    - + {% endfor %} {% endblock %} diff --git a/gestioncof/views.py b/gestioncof/views.py index 9a2fdc18..71dab9a3 100644 --- a/gestioncof/views.py +++ b/gestioncof/views.py @@ -20,14 +20,17 @@ from gestioncof.forms import UserProfileForm, EventStatusFilterForm, \ SurveyForm, SurveyStatusFilterForm, RegistrationUserForm, \ RegistrationProfileForm, AdminEventForm, EventForm +from bda.models import Tirage + import re @login_required def home(request): - data = {"surveys": Survey.objects.filter(old = False).all(), - "events": Event.objects.filter(old = False).all(), - "open_surveys": Survey.objects.filter(survey_open = True, old = False).all(), - "open_events": Event.objects.filter(registration_open = True, old = False).all()} + data = {"surveys": Survey.objects.filter(old=False).all(), + "events": Event.objects.filter(old=False).all(), + "open_surveys": Survey.objects.filter(survey_open=True, old=False).all(), + "open_events": Event.objects.filter(registration_open=True, old=False).all(), + "open_tirages": Tirage.objects.filter(active=True).all()} return render(request, "home.html", data) def login(request): @@ -496,7 +499,8 @@ def utile_cof(request): @buro_required def utile_bda(request): - return render(request, "utile_bda.html", {}) + tirages = Tirage.objects.all() + return render(request, "utile_bda.html", {'tirages': tirages}) @buro_required def liste_bdadiff(request):