Merge branch 'Kerl/fusion_bda' into 'master'
Kerl/fusion bda Ce patch fusionne les trois applications `bda`, `bda2`, `bda3` existantes en une seule qui peut gérer autant de tirage que souhaité par le BdA. Après avoir appliqué ce patch, il est nécessaire d'effectuer les migrations qui vont avec : `python manage.py migrate` Fixes #3 See merge request !17
This commit is contained in:
commit
fb16276ee3
56 changed files with 339 additions and 3277 deletions
77
bda/admin.py
77
bda/admin.py
|
@ -5,7 +5,9 @@ from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.db.models import Sum, Count
|
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):
|
class ChoixSpectacleInline(admin.TabularInline):
|
||||||
model = ChoixSpectacle
|
model = ChoixSpectacle
|
||||||
|
@ -37,62 +39,38 @@ class ParticipantAdmin(admin.ModelAdmin):
|
||||||
actions_on_bottom = True
|
actions_on_bottom = True
|
||||||
list_per_page = 400
|
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):
|
def send_attribs(self, request, queryset):
|
||||||
for member in queryset.all():
|
for member in queryset.all():
|
||||||
attribs = member.attributions.all()
|
attribs = member.attributions.all()
|
||||||
if len(attribs) == 0:
|
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 :
|
pour les spectacles suivants :
|
||||||
|
|
||||||
%s
|
%s
|
||||||
|
|
||||||
*Paiement*
|
*Paiement*
|
||||||
L'intégralité de ces places de spectacles est à régler à partir du lundi
|
L'intégralité de ces places de spectacles est à régler dès maintenant et AVANT
|
||||||
6 octobre et AVANT le vendredi 10 octobre, au bureau du COF pendant les
|
le %s, au bureau du COF pendant les heures de permanences (du lundi au vendredi
|
||||||
heures de permanences (du lundi au vendredi entre 12h et 14h, et entre 18h
|
entre 12h et 14h, et entre 18h et 20h). Des facilités de paiement sont bien
|
||||||
et 20h). Des facilités de paiement sont bien évidemment possibles : nous
|
évidemment possibles : nous pouvons ne pas encaisser le chèque immédiatement, ou
|
||||||
pouvons ne pas encaisser le chèque immédiatement, ou bien découper votre
|
bien découper votre paiement en deux fois. Pour ceux qui ne pourraient pas venir
|
||||||
paiement en deux fois. Pour ceux qui ne pourraient pas venir payer au bureau,
|
payer au bureau, merci de nous contacter par mail.
|
||||||
merci de nous contacter par mail.
|
|
||||||
|
|
||||||
*Mode de retrait des places*
|
*Mode de retrait des places*
|
||||||
Au moment du paiement, certaines places vous seront remises directement, d'autres
|
Au moment du paiement, certaines places vous seront remises directement, d'autres
|
||||||
|
@ -109,13 +87,13 @@ prochainement disponible, directement sur votre compte GestioCOF.
|
||||||
En vous souhaitant de très beaux spectacles tout au long de l'année,
|
En vous souhaitant de très beaux spectacles tout au long de l'année,
|
||||||
--
|
--
|
||||||
Le Bureau des Arts
|
Le Bureau des Arts
|
||||||
(Jean, Antoine, Élodie, Marion et Louise)
|
|
||||||
"""
|
"""
|
||||||
attribs_text = ""
|
attribs_text = ""
|
||||||
name = member.user.get_full_name()
|
name = member.user.get_full_name()
|
||||||
for attrib in attribs:
|
for attrib in attribs:
|
||||||
attribs_text += u"- 1 place pour %s\n" % attrib
|
attribs_text += u"- 1 place pour %s\n" % attrib
|
||||||
mail = mail % (name, attribs_text)
|
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,
|
send_mail ("Résultats du tirage au sort", mail,
|
||||||
"bda@ens.fr", [member.user.email],
|
"bda@ens.fr", [member.user.email],
|
||||||
fail_silently = True)
|
fail_silently = True)
|
||||||
|
@ -155,3 +133,4 @@ admin.site.register(Salle)
|
||||||
admin.site.register(Participant, ParticipantAdmin)
|
admin.site.register(Participant, ParticipantAdmin)
|
||||||
admin.site.register(Attribution, AttributionAdmin)
|
admin.site.register(Attribution, AttributionAdmin)
|
||||||
admin.site.register(ChoixSpectacle, ChoixSpectacleAdmin)
|
admin.site.register(ChoixSpectacle, ChoixSpectacleAdmin)
|
||||||
|
admin.site.register(Tirage)
|
||||||
|
|
|
@ -59,12 +59,6 @@ class Algorithm(object):
|
||||||
self.origranks[member][show],
|
self.origranks[member][show],
|
||||||
self.choices[member][show].double))
|
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):
|
def __call__(self, seed):
|
||||||
random.seed(seed)
|
random.seed(seed)
|
||||||
results = []
|
results = []
|
||||||
|
@ -102,3 +96,4 @@ class Algorithm(object):
|
||||||
self.IncrementRanks(member, i)
|
self.IncrementRanks(member, i)
|
||||||
results.append((show,winners,losers))
|
results.append((show,winners,losers))
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
|
|
||||||
from django import forms
|
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
|
from bda.models import Spectacle, Participant, ChoixSpectacle, Attribution
|
||||||
|
|
||||||
class BaseBdaFormSet(BaseInlineFormSet):
|
class BaseBdaFormSet(BaseInlineFormSet):
|
||||||
|
@ -19,7 +19,8 @@ class BaseBdaFormSet(BaseInlineFormSet):
|
||||||
spectacle = form.cleaned_data['spectacle']
|
spectacle = form.cleaned_data['spectacle']
|
||||||
delete = form.cleaned_data['DELETE']
|
delete = form.cleaned_data['DELETE']
|
||||||
if not delete and spectacle in spectacles:
|
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)
|
spectacles.append(spectacle)
|
||||||
|
|
||||||
class TokenForm(forms.Form):
|
class TokenForm(forms.Form):
|
||||||
|
@ -27,7 +28,8 @@ class TokenForm(forms.Form):
|
||||||
|
|
||||||
class SpectacleModelChoiceField(forms.ModelChoiceField):
|
class SpectacleModelChoiceField(forms.ModelChoiceField):
|
||||||
def label_from_instance(self, obj):
|
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):
|
class ResellForm(forms.Form):
|
||||||
count = forms.ChoiceField(choices = (("1","1"),("2","2"),))
|
count = forms.ChoiceField(choices = (("1","1"),("2","2"),))
|
||||||
|
|
56
bda/migrations/0002_add_tirage.py
Normal file
56
bda/migrations/0002_add_tirage.py
Normal file
|
@ -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,
|
||||||
|
),
|
||||||
|
]
|
|
@ -7,6 +7,19 @@ from django.contrib.auth.models import User
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.db.models.signals import post_save
|
from django.db.models.signals import post_save
|
||||||
|
|
||||||
|
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):
|
class Salle(models.Model):
|
||||||
name = models.CharField("Nom", max_length = 300)
|
name = models.CharField("Nom", max_length = 300)
|
||||||
address = models.TextField("Adresse")
|
address = models.TextField("Adresse")
|
||||||
|
@ -20,9 +33,10 @@ class Spectacle (models.Model):
|
||||||
location = models.ForeignKey(Salle)
|
location = models.ForeignKey(Salle)
|
||||||
description = models.TextField("Description", blank=True)
|
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)
|
price = models.FloatField("Prix d'une place")
|
||||||
slots = models.IntegerField("Places")
|
slots = models.IntegerField("Places")
|
||||||
priority = models.IntegerField("Priorité", default=1000)
|
priority = models.IntegerField("Priorité", default=1000)
|
||||||
|
tirage = models.ForeignKey(Tirage)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = "Spectacle"
|
verbose_name = "Spectacle"
|
||||||
|
@ -38,7 +52,8 @@ class Spectacle (models.Model):
|
||||||
return self.date.strftime('%d %b %Y %H:%M')
|
return self.date.strftime('%d %b %Y %H:%M')
|
||||||
|
|
||||||
def __unicode__ (self):
|
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 = (
|
PAYMENT_TYPES = (
|
||||||
("cash",u"Cash"),
|
("cash",u"Cash"),
|
||||||
|
@ -48,11 +63,17 @@ PAYMENT_TYPES = (
|
||||||
)
|
)
|
||||||
|
|
||||||
class Participant(models.Model):
|
class Participant(models.Model):
|
||||||
user = models.OneToOneField(User)
|
user = models.ForeignKey(User)
|
||||||
choices = models.ManyToManyField(Spectacle, through = "ChoixSpectacle", related_name = "chosen_by")
|
choices = models.ManyToManyField(Spectacle,
|
||||||
attributions = models.ManyToManyField(Spectacle, through = "Attribution", related_name = "attributed_to")
|
through="ChoixSpectacle",
|
||||||
|
related_name="chosen_by")
|
||||||
|
attributions = models.ManyToManyField(Spectacle,
|
||||||
|
through="Attribution",
|
||||||
|
related_name="attributed_to")
|
||||||
paid = models.BooleanField (u"A payé", default=False)
|
paid = models.BooleanField (u"A payé", default=False)
|
||||||
paymenttype = models.CharField(u"Moyen de paiement", max_length = 6, choices = PAYMENT_TYPES, blank = True)
|
paymenttype = models.CharField(u"Moyen de paiement",
|
||||||
|
max_length=6, choices=PAYMENT_TYPES, blank=True)
|
||||||
|
tirage = models.ForeignKey(Tirage)
|
||||||
|
|
||||||
def __unicode__ (self):
|
def __unicode__ (self):
|
||||||
return u"%s" % (self.user)
|
return u"%s" % (self.user)
|
||||||
|
@ -67,7 +88,8 @@ class ChoixSpectacle (models.Model):
|
||||||
participant = models.ForeignKey(Participant)
|
participant = models.ForeignKey(Participant)
|
||||||
spectacle = models.ForeignKey(Spectacle, related_name="participants")
|
spectacle = models.ForeignKey(Spectacle, related_name="participants")
|
||||||
priority = models.PositiveIntegerField("Priorité")
|
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):
|
def get_double(self):
|
||||||
return self.double_choice != "1"
|
return self.double_choice != "1"
|
||||||
|
@ -90,3 +112,4 @@ class Attribution (models.Model):
|
||||||
|
|
||||||
def __unicode__ (self):
|
def __unicode__ (self):
|
||||||
return u"%s -- %s" % (self.participant, self.spectacle)
|
return u"%s -- %s" % (self.participant, self.spectacle)
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
{% block extracontent %}
|
{% block extracontent %}
|
||||||
|
|
||||||
<h1>Attribution (détails)</h1>
|
<h1>Attribution (détails)</h1>
|
||||||
<h2>Token : {{ token }}</h2>
|
<h2>Token :</h2>
|
||||||
|
<pre>{{ token }}</pre>
|
||||||
<h2>Placés : {{ total_slots }} ; Déçus : {{ total_losers }}</h2>
|
<h2>Placés : {{ total_slots }} ; Déçus : {{ total_losers }}</h2>
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
|
|
|
@ -8,7 +8,8 @@
|
||||||
{% block realcontent %}
|
{% block realcontent %}
|
||||||
|
|
||||||
<h1>Attribution</h1>
|
<h1>Attribution</h1>
|
||||||
<h2>Token : {{ token }}</h2>
|
<h2>Token :</h2>
|
||||||
|
<pre>{{ token }}</pre>
|
||||||
<h2>Placés : {{ total_slots }} ; Déçus : {{ total_losers }}</h2>
|
<h2>Placés : {{ total_slots }} ; Déçus : {{ total_losers }}</h2>
|
||||||
{% if user.profile.is_buro %}<h2>Déficit total: {{ total_deficit }} €, Opéra: {{ opera_deficit }} €, Attribué: {{ total_sold }} €</h2>{% endif %}
|
{% if user.profile.is_buro %}<h2>Déficit total: {{ total_deficit }} €, Opéra: {{ opera_deficit }} €, Attribué: {{ total_sold }} €</h2>{% endif %}
|
||||||
<h2>Temps de calcul : {{ duration|floatformat }}s</h2>
|
<h2>Temps de calcul : {{ duration|floatformat }}s</h2>
|
||||||
|
|
|
@ -98,7 +98,7 @@ var django = {
|
||||||
{% if stateerror %}
|
{% if stateerror %}
|
||||||
<p class="error">Impossible d'enregistrer vos modifications: vous avez apporté d'autres modifications entre temps</p>
|
<p class="error">Impossible d'enregistrer vos modifications: vous avez apporté d'autres modifications entre temps</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<form id="bda_form" method="post" action="{% url 'bda-tirage-inscription' %}">
|
<form id="bda_form" method="post" action="{% url 'bda-tirage-inscription' tirage.id %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% include "inscription-formset.html" %}
|
{% include "inscription-formset.html" %}
|
||||||
<input type="button" class="btn-addmore" value="Ajouter un autre vœu" id="add_more">
|
<input type="button" class="btn-addmore" value="Ajouter un autre vœu" id="add_more">
|
||||||
|
|
|
@ -1,116 +0,0 @@
|
||||||
{% extends "base_title.html" %}
|
|
||||||
|
|
||||||
{% block extra_head %}
|
|
||||||
<link href="{{ STATIC_URL }}grappelli/jquery/ui/css/custom-theme/jquery-ui-1.8.custom.css" rel="stylesheet" type="text/css" media="screen" title="no title" charset="utf-8" />
|
|
||||||
<script src="{{ STATIC_URL }}grappelli/jquery/jquery-1.6.2.min.js" type="text/javascript"></script>
|
|
||||||
<script src="{{ STATIC_URL }}grappelli/jquery/ui/js/jquery-ui-1.8.15.custom.min.js" type="text/javascript"></script>
|
|
||||||
<link href="{{ STATIC_URL }}grappelli/css/tools.css" rel="stylesheet" type="text/css" />
|
|
||||||
<link href="{{ STATIC_URL }}grappelli/css/jquery-ui-grappelli-extensions.css" rel="stylesheet" type="text/css" />
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block realcontent %}
|
|
||||||
<script type="text/javascript">
|
|
||||||
var django = {
|
|
||||||
"jQuery": jQuery.noConflict(true)
|
|
||||||
};
|
|
||||||
|
|
||||||
(function($) {
|
|
||||||
cloneMore = function(selector, type) {
|
|
||||||
var newElement = $(selector).clone(true);
|
|
||||||
var total = $('#id_' + type + '-TOTAL_FORMS').val();
|
|
||||||
newElement.find(':input').each(function() {
|
|
||||||
var name = $(this).attr('name').replace('-' + (total-1) + '-','-' + total + '-');
|
|
||||||
var id = 'id_' + name;
|
|
||||||
$(this).attr({'name': name, 'id': id}).val('').removeAttr('checked');
|
|
||||||
});
|
|
||||||
newElement.find('label').each(function() {
|
|
||||||
var newFor = $(this).attr('for').replace('-' + (total-1) + '-','-' + total + '-');
|
|
||||||
$(this).attr('for', newFor);
|
|
||||||
});
|
|
||||||
total++;
|
|
||||||
$('#id_' + type + '-TOTAL_FORMS').val(total);
|
|
||||||
$(selector).after(newElement);
|
|
||||||
}
|
|
||||||
deleteButtonHandler = function(elem) {
|
|
||||||
elem.bind("click", function() {
|
|
||||||
var deleteInput = $(this).prev().prev(),
|
|
||||||
form = $(this).parents(".dynamic-form").first();
|
|
||||||
// callback
|
|
||||||
// toggle options.predeleteCssClass and toggle checkbox
|
|
||||||
if (form.hasClass("has_original")) {
|
|
||||||
form.toggleClass("predelete");
|
|
||||||
if (deleteInput.attr("checked")) {
|
|
||||||
deleteInput.attr("checked", false);
|
|
||||||
} else {
|
|
||||||
deleteInput.attr("checked", true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// callback
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$(document).ready(function($) {
|
|
||||||
deleteButtonHandler($("table#bda_formset tbody.bda_formset_content").find("a.delete-handler"));
|
|
||||||
$("table#bda_formset tbody.bda_formset_content").sortable({
|
|
||||||
handle: "a.drag-handler",
|
|
||||||
items: "tr",
|
|
||||||
axis: "y",
|
|
||||||
appendTo: 'body',
|
|
||||||
forceHelperSize: true,
|
|
||||||
placeholder: 'ui-sortable-placeholder',
|
|
||||||
forcePlaceholderSize: true,
|
|
||||||
containment: 'form#bda_form',
|
|
||||||
tolerance: 'pointer',
|
|
||||||
start: function(evt, ui) {
|
|
||||||
var template = "",
|
|
||||||
len = ui.item.children("td").length;
|
|
||||||
for (var i = 0; i < len; i++) {
|
|
||||||
template += "<td style='height:" + (ui.item.outerHeight() + 12 ) + "px' class='placeholder-cell'> </td>"
|
|
||||||
}
|
|
||||||
template += "";
|
|
||||||
ui.placeholder.html(template);
|
|
||||||
},
|
|
||||||
stop: function(evt, ui) {
|
|
||||||
// Toggle div.table twice to remove webkits border-spacing bug
|
|
||||||
$("table#bda_formset").toggle().toggle();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
$("#bda_form").bind("submit", function(){
|
|
||||||
var sortable_field_name = "priority";
|
|
||||||
var i = 1;
|
|
||||||
$(".bda_formset_content").find("tr").each(function(){
|
|
||||||
var fields = $(this).find("td :input[value]"),
|
|
||||||
select = $(this).find("td select");
|
|
||||||
if (select.val() && fields.serialize()) {
|
|
||||||
$(this).find("input[name$='"+sortable_field_name+"']").val(i);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
})(django.jQuery);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<h2>Inscription au tirage au sort du BDA</h2>
|
|
||||||
{% if success %}
|
|
||||||
<p class="success">Votre inscription a été mise à jour avec succès !</p>
|
|
||||||
{% endif %}
|
|
||||||
<form id="bda_form" method="post" action="{% url 'bda-tirage-inscription' %}">
|
|
||||||
{% csrf_token %}
|
|
||||||
{% include "inscription-formset.html" %}
|
|
||||||
<input type="button" class="btn-addmore" value="Ajouter un autre vœu" id="add_more">
|
|
||||||
<script>
|
|
||||||
django.jQuery('#add_more').click(function() {
|
|
||||||
cloneMore('tbody.bda_formset_content tr:last-child', 'choixspectacle_set');
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<input type="submit" class="btn-submit" value="Enregistrer" />
|
|
||||||
Prix total actuel : {{ total_price }}€
|
|
||||||
<hr />
|
|
||||||
<p class="footnotes">
|
|
||||||
<sup>1</sup>: demander deux places pour ce spectacle<br />
|
|
||||||
<sup>2</sup>: abandonner une place si impossible d'en obtenir une seconde pour ce spectacle (si vous avez coché l'option <tt>Deux places</tt> pour ce spectacle)<br />
|
|
||||||
<sup>3</sup>: 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<br />
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
|
|
@ -1,120 +0,0 @@
|
||||||
{% extends "base_title.html" %}
|
|
||||||
|
|
||||||
{% block extra_head %}
|
|
||||||
<link href="{{ STATIC_URL }}grappelli/jquery/ui/css/custom-theme/jquery-ui-1.8.custom.css" rel="stylesheet" type="text/css" media="screen" title="no title" charset="utf-8" />
|
|
||||||
<script src="{{ STATIC_URL }}grappelli/jquery/jquery-1.6.2.min.js" type="text/javascript"></script>
|
|
||||||
<script src="{{ STATIC_URL }}grappelli/jquery/ui/js/jquery-ui-1.8.15.custom.min.js" type="text/javascript"></script>
|
|
||||||
<link href="{{ STATIC_URL }}grappelli/css/tools.css" rel="stylesheet" type="text/css" />
|
|
||||||
<link href="{{ STATIC_URL }}grappelli/css/jquery-ui-grappelli-extensions.css" rel="stylesheet" type="text/css" />
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block realcontent %}
|
|
||||||
<script type="text/javascript">
|
|
||||||
var django = {
|
|
||||||
"jQuery": jQuery.noConflict(true)
|
|
||||||
};
|
|
||||||
|
|
||||||
(function($) {
|
|
||||||
cloneMore = function(selector, type) {
|
|
||||||
var newElement = $(selector).clone(true);
|
|
||||||
var total = $('#id_' + type + '-TOTAL_FORMS').val();
|
|
||||||
newElement.find(':input').each(function() {
|
|
||||||
var name = $(this).attr('name').replace('-' + (total-1) + '-','-' + total + '-');
|
|
||||||
var id = 'id_' + name;
|
|
||||||
$(this).attr({'name': name, 'id': id}).val('').removeAttr('checked');
|
|
||||||
});
|
|
||||||
newElement.find('label').each(function() {
|
|
||||||
var newFor = $(this).attr('for').replace('-' + (total-1) + '-','-' + total + '-');
|
|
||||||
$(this).attr('for', newFor);
|
|
||||||
});
|
|
||||||
total++;
|
|
||||||
$('#id_' + type + '-TOTAL_FORMS').val(total);
|
|
||||||
$(selector).after(newElement);
|
|
||||||
}
|
|
||||||
deleteButtonHandler = function(elem) {
|
|
||||||
elem.bind("click", function() {
|
|
||||||
var deleteInput = $(this).prev().prev(),
|
|
||||||
form = $(this).parents(".dynamic-form").first();
|
|
||||||
// callback
|
|
||||||
// toggle options.predeleteCssClass and toggle checkbox
|
|
||||||
if (form.hasClass("has_original")) {
|
|
||||||
form.toggleClass("predelete");
|
|
||||||
if (deleteInput.attr("checked")) {
|
|
||||||
deleteInput.attr("checked", false);
|
|
||||||
} else {
|
|
||||||
deleteInput.attr("checked", true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// callback
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$(document).ready(function($) {
|
|
||||||
deleteButtonHandler($("table#bda_formset tbody.bda_formset_content").find("a.delete-handler"));
|
|
||||||
$("table#bda_formset tbody.bda_formset_content").sortable({
|
|
||||||
handle: "a.drag-handler",
|
|
||||||
items: "tr",
|
|
||||||
axis: "y",
|
|
||||||
appendTo: 'body',
|
|
||||||
forceHelperSize: true,
|
|
||||||
placeholder: 'ui-sortable-placeholder',
|
|
||||||
forcePlaceholderSize: true,
|
|
||||||
containment: 'form#bda_form',
|
|
||||||
tolerance: 'pointer',
|
|
||||||
start: function(evt, ui) {
|
|
||||||
var template = "",
|
|
||||||
len = ui.item.children("td").length;
|
|
||||||
for (var i = 0; i < len; i++) {
|
|
||||||
template += "<td style='height:" + (ui.item.outerHeight() + 12 ) + "px' class='placeholder-cell'> </td>"
|
|
||||||
}
|
|
||||||
template += "";
|
|
||||||
ui.placeholder.html(template);
|
|
||||||
},
|
|
||||||
stop: function(evt, ui) {
|
|
||||||
// Toggle div.table twice to remove webkits border-spacing bug
|
|
||||||
$("table#bda_formset").toggle().toggle();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
$("#bda_form").bind("submit", function(){
|
|
||||||
var sortable_field_name = "priority";
|
|
||||||
var i = 1;
|
|
||||||
$(".bda_formset_content").find("tr").each(function(){
|
|
||||||
var fields = $(this).find("td :input[value]"),
|
|
||||||
select = $(this).find("td select");
|
|
||||||
if (select.val() && fields.serialize()) {
|
|
||||||
$(this).find("input[name$='"+sortable_field_name+"']").val(i);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
})(django.jQuery);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<h2>Inscription au tirage au sort du BdA</h2>
|
|
||||||
{% if success %}
|
|
||||||
<p class="success">Votre inscription a été mise à jour avec succès !</p>
|
|
||||||
{% endif %}
|
|
||||||
{% if stateerror %}
|
|
||||||
<p class="error">Impossible d'enregistrer vos modifications: vous avez apporté d'autres modifications entre temps</p>
|
|
||||||
{% endif %}
|
|
||||||
<form id="bda_form" method="post" action="{% url 'bda2-tirage-inscription' %}">
|
|
||||||
{% csrf_token %}
|
|
||||||
{% include "inscription-formset.html" %}
|
|
||||||
<input type="button" class="btn-addmore" value="Ajouter un autre vœu" id="add_more">
|
|
||||||
<script>
|
|
||||||
django.jQuery('#add_more').click(function() {
|
|
||||||
cloneMore('tbody.bda_formset_content tr:last-child', 'choixspectacle_set');
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<input type="hidden" name="dbstate" value="{{ dbstate }}" />
|
|
||||||
<input type="submit" class="btn-submit" value="Enregistrer" />
|
|
||||||
Prix total actuel : {{ total_price }}€
|
|
||||||
<hr />
|
|
||||||
<p class="footnotes">
|
|
||||||
<sup>1</sup>: demander deux places pour ce spectable<br />
|
|
||||||
<sup>2</sup>: abandonner une place si impossible d'en obtenir une seconde pour ce spectacle (si vous avez coché l'option <tt>Deux places</tt> pour ce spectacle)<br />
|
|
||||||
<sup>3</sup>: 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<br />
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
|
|
@ -1,120 +0,0 @@
|
||||||
{% extends "base_title.html" %}
|
|
||||||
|
|
||||||
{% block extra_head %}
|
|
||||||
<link href="{{ STATIC_URL }}grappelli/jquery/ui/css/custom-theme/jquery-ui-1.8.custom.css" rel="stylesheet" type="text/css" media="screen" title="no title" charset="utf-8" />
|
|
||||||
<script src="{{ STATIC_URL }}grappelli/jquery/jquery-1.6.2.min.js" type="text/javascript"></script>
|
|
||||||
<script src="{{ STATIC_URL }}grappelli/jquery/ui/js/jquery-ui-1.8.15.custom.min.js" type="text/javascript"></script>
|
|
||||||
<link href="{{ STATIC_URL }}grappelli/css/tools.css" rel="stylesheet" type="text/css" />
|
|
||||||
<link href="{{ STATIC_URL }}grappelli/css/jquery-ui-grappelli-extensions.css" rel="stylesheet" type="text/css" />
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block realcontent %}
|
|
||||||
<script type="text/javascript">
|
|
||||||
var django = {
|
|
||||||
"jQuery": jQuery.noConflict(true)
|
|
||||||
};
|
|
||||||
|
|
||||||
(function($) {
|
|
||||||
cloneMore = function(selector, type) {
|
|
||||||
var newElement = $(selector).clone(true);
|
|
||||||
var total = $('#id_' + type + '-TOTAL_FORMS').val();
|
|
||||||
newElement.find(':input').each(function() {
|
|
||||||
var name = $(this).attr('name').replace('-' + (total-1) + '-','-' + total + '-');
|
|
||||||
var id = 'id_' + name;
|
|
||||||
$(this).attr({'name': name, 'id': id}).val('').removeAttr('checked');
|
|
||||||
});
|
|
||||||
newElement.find('label').each(function() {
|
|
||||||
var newFor = $(this).attr('for').replace('-' + (total-1) + '-','-' + total + '-');
|
|
||||||
$(this).attr('for', newFor);
|
|
||||||
});
|
|
||||||
total++;
|
|
||||||
$('#id_' + type + '-TOTAL_FORMS').val(total);
|
|
||||||
$(selector).after(newElement);
|
|
||||||
}
|
|
||||||
deleteButtonHandler = function(elem) {
|
|
||||||
elem.bind("click", function() {
|
|
||||||
var deleteInput = $(this).prev().prev(),
|
|
||||||
form = $(this).parents(".dynamic-form").first();
|
|
||||||
// callback
|
|
||||||
// toggle options.predeleteCssClass and toggle checkbox
|
|
||||||
if (form.hasClass("has_original")) {
|
|
||||||
form.toggleClass("predelete");
|
|
||||||
if (deleteInput.attr("checked")) {
|
|
||||||
deleteInput.attr("checked", false);
|
|
||||||
} else {
|
|
||||||
deleteInput.attr("checked", true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// callback
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$(document).ready(function($) {
|
|
||||||
deleteButtonHandler($("table#bda_formset tbody.bda_formset_content").find("a.delete-handler"));
|
|
||||||
$("table#bda_formset tbody.bda_formset_content").sortable({
|
|
||||||
handle: "a.drag-handler",
|
|
||||||
items: "tr",
|
|
||||||
axis: "y",
|
|
||||||
appendTo: 'body',
|
|
||||||
forceHelperSize: true,
|
|
||||||
placeholder: 'ui-sortable-placeholder',
|
|
||||||
forcePlaceholderSize: true,
|
|
||||||
containment: 'form#bda_form',
|
|
||||||
tolerance: 'pointer',
|
|
||||||
start: function(evt, ui) {
|
|
||||||
var template = "",
|
|
||||||
len = ui.item.children("td").length;
|
|
||||||
for (var i = 0; i < len; i++) {
|
|
||||||
template += "<td style='height:" + (ui.item.outerHeight() + 12 ) + "px' class='placeholder-cell'> </td>"
|
|
||||||
}
|
|
||||||
template += "";
|
|
||||||
ui.placeholder.html(template);
|
|
||||||
},
|
|
||||||
stop: function(evt, ui) {
|
|
||||||
// Toggle div.table twice to remove webkits border-spacing bug
|
|
||||||
$("table#bda_formset").toggle().toggle();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
$("#bda_form").bind("submit", function(){
|
|
||||||
var sortable_field_name = "priority";
|
|
||||||
var i = 1;
|
|
||||||
$(".bda_formset_content").find("tr").each(function(){
|
|
||||||
var fields = $(this).find("td :input[value]"),
|
|
||||||
select = $(this).find("td select");
|
|
||||||
if (select.val() && fields.serialize()) {
|
|
||||||
$(this).find("input[name$='"+sortable_field_name+"']").val(i);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
})(django.jQuery);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<h2>Inscription au tirage au sort du BdA</h2>
|
|
||||||
{% if success %}
|
|
||||||
<p class="success">Votre inscription a été mise à jour avec succès !</p>
|
|
||||||
{% endif %}
|
|
||||||
{% if stateerror %}
|
|
||||||
<p class="error">Impossible d'enregistrer vos modifications: vous avez apporté d'autres modifications entre temps</p>
|
|
||||||
{% endif %}
|
|
||||||
<form id="bda_form" method="post" action="{% url 'bda3-tirage-inscription' %}">
|
|
||||||
{% csrf_token %}
|
|
||||||
{% include "inscription-formset.html" %}
|
|
||||||
<input type="button" class="btn-addmore" value="Ajouter un autre vœu" id="add_more">
|
|
||||||
<script>
|
|
||||||
django.jQuery('#add_more').click(function() {
|
|
||||||
cloneMore('tbody.bda_formset_content tr:last-child', 'choixspectacle_set');
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<input type="hidden" name="dbstate" value="{{ dbstate }}" />
|
|
||||||
<input type="submit" class="btn-submit" value="Enregistrer" />
|
|
||||||
Prix total actuel : {{ total_price }}€
|
|
||||||
<hr />
|
|
||||||
<p class="footnotes">
|
|
||||||
<sup>1</sup>: demander deux places pour ce spectable<br />
|
|
||||||
<sup>2</sup>: abandonner une place si impossible d'en obtenir une seconde pour ce spectacle (si vous avez coché l'option <tt>Deux places</tt> pour ce spectacle)<br />
|
|
||||||
<sup>3</sup>: 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<br />
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
|
|
@ -12,7 +12,7 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ol>
|
</ol>
|
||||||
<h4>Total à payer : {{ total|floatformat }}€</h4>
|
<h4>Total à payer : {{ total|floatformat }}€</h4>
|
||||||
<h4><a href="{% url "bda-places-attribuees-ics" %}">Exporter au format calendrier</a> (.ics, compatible avec tous les logiciels d'agenda)</h4>
|
<h4><a href="{% url "bda-places-attribuees-ics" tirage.id %}">Exporter au format calendrier</a> (.ics, compatible avec tous les logiciels d'agenda)</h4>
|
||||||
{% else %}
|
{% else %}
|
||||||
<h3>Vous n'avez aucune place :(</h3>
|
<h3>Vous n'avez aucune place :(</h3>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
{% block realcontent %}
|
{% block realcontent %}
|
||||||
<h1><strong>Spectacles</strong></h1>
|
<h1><strong>Spectacles</strong></h1>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="{% url 'bda-unpaid' %}">Pas payé</a></li>
|
<li><a href="{% url 'bda-unpaid' tirage_id %}">Pas payé</a></li>
|
||||||
{% for spectacle in object_list %}
|
{% for spectacle in object_list %}
|
||||||
<li><a href="{% url 'bda-spectacle' spectacle.id %}">{{ spectacle }}</a></li>
|
<li><a href="{% url 'bda-spectacle' tirage_id spectacle.id %}">{{ spectacle }}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
168
bda/views.py
168
bda/views.py
|
@ -6,25 +6,39 @@ from django.contrib.auth.decorators import login_required
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.core import serializers
|
from django.core import serializers
|
||||||
|
from django.forms.models import inlineformset_factory
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
from django.core.mail import send_mail
|
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
|
import time
|
||||||
|
|
||||||
from gestioncof.decorators import cof_required, buro_required
|
from gestioncof.decorators import cof_required, buro_required
|
||||||
from gestioncof.shared import send_custom_mail
|
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.algorithm import Algorithm
|
||||||
|
|
||||||
from bda.forms import BaseBdaFormSet, TokenForm, ResellForm
|
from bda.forms import BaseBdaFormSet, TokenForm, ResellForm
|
||||||
|
|
||||||
@cof_required
|
@cof_required
|
||||||
def etat_places(request):
|
def etat_places(request, tirage_id):
|
||||||
spectacles1 = ChoixSpectacle.objects.filter(double_choice = "1").all().values('spectacle','spectacle__title').annotate(total = models.Count('spectacle'))
|
tirage = get_object_or_404(Tirage, id=tirage_id)
|
||||||
spectacles2 = ChoixSpectacle.objects.exclude(double_choice = "1").all().values('spectacle','spectacle__title').annotate(total = models.Count('spectacle'))
|
spectacles1 = ChoixSpectacle.objects \
|
||||||
spectacles = Spectacle.objects.all()
|
.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 = {}
|
spectacles_dict = {}
|
||||||
total = 0
|
total = 0
|
||||||
for spectacle in spectacles:
|
for spectacle in spectacles:
|
||||||
|
@ -33,13 +47,18 @@ def etat_places(request):
|
||||||
spectacles_dict[spectacle.id] = spectacle
|
spectacles_dict[spectacle.id] = spectacle
|
||||||
for spectacle in spectacles1:
|
for spectacle in spectacles1:
|
||||||
spectacles_dict[spectacle["spectacle"]].total += spectacle["total"]
|
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"]
|
total += spectacle["total"]
|
||||||
for spectacle in spectacles2:
|
for spectacle in spectacles2:
|
||||||
spectacles_dict[spectacle["spectacle"]].total += 2*spectacle["total"]
|
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"]
|
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):
|
def _hash_queryset(queryset):
|
||||||
data = serializers.serialize("json", queryset)
|
data = serializers.serialize("json", queryset)
|
||||||
|
@ -48,9 +67,12 @@ def _hash_queryset(queryset):
|
||||||
return hasher.hexdigest()
|
return hasher.hexdigest()
|
||||||
|
|
||||||
@cof_required
|
@cof_required
|
||||||
def places(request):
|
def places(request, tirage_id):
|
||||||
participant, created = Participant.objects.get_or_create(user = request.user)
|
tirage = get_object_or_404(Tirage, id=tirage_id)
|
||||||
places = participant.attribution_set.order_by("spectacle__date", "spectacle").all()
|
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])
|
total = sum([place.spectacle.price for place in places])
|
||||||
filtered_places = []
|
filtered_places = []
|
||||||
places_dict = {}
|
places_dict = {}
|
||||||
|
@ -73,13 +95,17 @@ def places(request):
|
||||||
return render(request, "resume_places.html",
|
return render(request, "resume_places.html",
|
||||||
{"participant": participant,
|
{"participant": participant,
|
||||||
"places": filtered_places,
|
"places": filtered_places,
|
||||||
|
"tirage": tirage,
|
||||||
"total": total,
|
"total": total,
|
||||||
"warning": warning})
|
"warning": warning})
|
||||||
|
|
||||||
@cof_required
|
@cof_required
|
||||||
def places_ics(request):
|
def places_ics(request, tirage_id):
|
||||||
participant, created = Participant.objects.get_or_create(user = request.user)
|
tirage = get_object_or_404(Tirage, id=tirage_id)
|
||||||
places = participant.attribution_set.order_by("spectacle__date", "spectacle").all()
|
participant, created = Participant.objects.get_or_create(
|
||||||
|
user=request.user, tirage=tirage)
|
||||||
|
places = participant.attribution_set.order_by(
|
||||||
|
"spectacle__date", "spectacle").all()
|
||||||
filtered_places = []
|
filtered_places = []
|
||||||
places_dict = {}
|
places_dict = {}
|
||||||
spectacles = []
|
spectacles = []
|
||||||
|
@ -98,13 +124,34 @@ def places_ics(request):
|
||||||
"places": filtered_places}, content_type="text/calendar")
|
"places": filtered_places}, content_type="text/calendar")
|
||||||
|
|
||||||
@cof_required
|
@cof_required
|
||||||
def inscription(request):
|
def inscription(request, tirage_id):
|
||||||
if datetime.now() > datetime(2015, 10, 4, 12, 00):
|
tirage = get_object_or_404(Tirage, id=tirage_id)
|
||||||
participant, created = Participant.objects.get_or_create(user = request.user)
|
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()
|
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})
|
return render(request, "resume_inscription.html",
|
||||||
BdaFormSet = inlineformset_factory(Participant, ChoixSpectacle, fields = ("spectacle","double_choice","priority",), formset = BaseBdaFormSet)
|
{ "error_title": "C'est fini !",
|
||||||
participant, created = Participant.objects.get_or_create(user = request.user)
|
"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
|
success = False
|
||||||
stateerror = False
|
stateerror = False
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
|
@ -115,7 +162,6 @@ def inscription(request):
|
||||||
else:
|
else:
|
||||||
formset = BdaFormSet(request.POST, instance=participant)
|
formset = BdaFormSet(request.POST, instance=participant)
|
||||||
if formset.is_valid():
|
if formset.is_valid():
|
||||||
#ChoixSpectacle.objects.filter(participant = participant).delete()
|
|
||||||
formset.save()
|
formset.save()
|
||||||
success = True
|
success = True
|
||||||
formset = BdaFormSet(instance=participant)
|
formset = BdaFormSet(instance=participant)
|
||||||
|
@ -126,17 +172,27 @@ def inscription(request):
|
||||||
for choice in participant.choixspectacle_set.all():
|
for choice in participant.choixspectacle_set.all():
|
||||||
total_price += choice.spectacle.price
|
total_price += choice.spectacle.price
|
||||||
if choice.double: 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)
|
form = TokenForm(request.POST)
|
||||||
if not form.is_valid():
|
if not form.is_valid():
|
||||||
return tirage(request)
|
return tirage(request)
|
||||||
|
tirage_elt.token = form.cleaned_data['token']
|
||||||
|
tirage_elt.save()
|
||||||
start = time.time()
|
start = time.time()
|
||||||
data = {}
|
data = {}
|
||||||
shows = Spectacle.objects.select_related().all()
|
shows = tirage_elt.spectacle_set.select_related().all()
|
||||||
members = Participant.objects.all()
|
members = tirage_elt.spectacle_set.all()
|
||||||
choices = ChoixSpectacle.objects.order_by('participant', 'priority').select_related().all()
|
choices = ChoixSpectacle.objects.filter(spectacle__tirage=tirage_elt).order_by(
|
||||||
|
'participant', 'priority').select_related().all()
|
||||||
algo = Algorithm(shows, members, choices)
|
algo = Algorithm(shows, members, choices)
|
||||||
results = algo(form.cleaned_data["token"])
|
results = algo(form.cleaned_data["token"])
|
||||||
total_slots = 0
|
total_slots = 0
|
||||||
|
@ -179,7 +235,10 @@ def do_tirage(request):
|
||||||
member.total += show.price
|
member.total += show.price
|
||||||
members2 = members2.items()
|
members2 = members2.items()
|
||||||
data["members2"] = sorted(members2, key=lambda m: m[0].user.last_name)
|
data["members2"] = sorted(members2, key=lambda m: m[0].user.last_name)
|
||||||
if False and request.user.username in ["seguin", "harazi","fromherz", "ccadiou"]:
|
# À partir d'ici, le tirage devient effectif
|
||||||
|
# FIXME: Établir les conditions de validations (formulaire ?)
|
||||||
|
# cf. issue #32
|
||||||
|
if False:
|
||||||
Attribution.objects.all().delete()
|
Attribution.objects.all().delete()
|
||||||
for (show, members, _) in results:
|
for (show, members, _) in results:
|
||||||
for (member, _, _, _) in members:
|
for (member, _, _, _) in members:
|
||||||
|
@ -189,12 +248,12 @@ def do_tirage(request):
|
||||||
else:
|
else:
|
||||||
return render(request, "bda-attrib.html", data)
|
return render(request, "bda-attrib.html", data)
|
||||||
|
|
||||||
@login_required
|
@buro_required
|
||||||
def tirage(request):
|
def tirage(request, tirage_id):
|
||||||
if request.POST:
|
if request.POST:
|
||||||
form = TokenForm(request.POST)
|
form = TokenForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
return do_tirage(request)
|
return do_tirage(request, tirage_id)
|
||||||
else:
|
else:
|
||||||
form = TokenForm()
|
form = TokenForm()
|
||||||
return render(request, "bda-token.html", {"form": form})
|
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€.
|
Je souhaite revendre %s pour %s le %s (%s) à %.02f€.
|
||||||
Contactez moi par email si vous êtes intéressé·e·s !
|
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,
|
send_mail("%s" % spectacle, mail,
|
||||||
request.user.email, ["bda-revente@lists.ens.fr"],
|
request.user.email, ["bda-revente@lists.ens.fr"],
|
||||||
fail_silently = False)
|
fail_silently = False)
|
||||||
return render(request, "bda-success.html", {"show": spectacle, "places": places})
|
return render(request, "bda-success.html", {"show": spectacle, "places": places})
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def revente(request):
|
def revente(request, tirage_id):
|
||||||
participant, created = Participant.objects.get_or_create(user = request.user)
|
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:
|
if not participant.paid:
|
||||||
return render(request, "bda-notpaid.html", {})
|
return render(request, "bda-notpaid.html", {})
|
||||||
if request.POST:
|
if request.POST:
|
||||||
|
@ -237,21 +300,40 @@ def revente(request):
|
||||||
return do_resell(request, form)
|
return do_resell(request, form)
|
||||||
else:
|
else:
|
||||||
form = ResellForm(participant)
|
form = ResellForm(participant)
|
||||||
return render(request, "bda-revente.html", {"form": form})
|
return render(request, "bda-revente.html", {"form": form, 'tirage': tirage})
|
||||||
|
|
||||||
@buro_required
|
@buro_required
|
||||||
def spectacle(request, spectacle_id):
|
def spectacle(request, tirage_id, spectacle_id):
|
||||||
spectacle = get_object_or_404(Spectacle, 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})
|
return render(request, "bda-emails.html", {"spectacle": spectacle})
|
||||||
|
|
||||||
@buro_required
|
|
||||||
def unpaid(request):
|
class SpectacleListView(ListView):
|
||||||
return render(request, "bda-unpaid.html", {"unpaid": Participant.objects.filter(paid = False).all()})
|
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
|
@buro_required
|
||||||
def liste_spectacles_ics(request):
|
def unpaid(request, tirage_id):
|
||||||
spectacles = Spectacle.objects.order_by("date").all()
|
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, tirage_id):
|
||||||
|
tirage = get_object_or_404(Tirage, id=tirage_id)
|
||||||
|
spectacles = tirage.spectacle_set.order_by("date").all()
|
||||||
for spectacle in spectacles:
|
for spectacle in spectacles:
|
||||||
spectacle.dtend = spectacle.date + timedelta(seconds=7200)
|
spectacle.dtend = spectacle.date + timedelta(seconds=7200)
|
||||||
return render(request, "liste_spectacles.ics",
|
return render(request, "liste_spectacles.ics",
|
||||||
{"spectacles": spectacles}, content_type="text/calendar")
|
{"spectacles": spectacles, "tirage": tirage},
|
||||||
|
content_type="text/calendar")
|
||||||
|
|
||||||
|
|
177
bda2/admin.py
177
bda2/admin.py
|
@ -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)
|
|
|
@ -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
|
|
|
@ -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...'})
|
|
421
bda2/difftobda
421
bda2/difftobda
|
@ -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~
|
|
|
@ -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()
|
|
||||||
|
|
|
@ -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 places<sup>1</sup>')),
|
|
||||||
('autoquit', models.BooleanField(default=False, verbose_name=b'Abandon<sup>2</sup>')),
|
|
||||||
],
|
|
||||||
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')]),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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 places<sup>1</sup>",default=False)
|
|
||||||
autoquit = models.BooleanField("Abandon<sup>2</sup>",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)
|
|
|
@ -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;}
|
|
|
@ -1,27 +0,0 @@
|
||||||
{% extends "bda-attrib.html" %}
|
|
||||||
|
|
||||||
{% block extracontent %}
|
|
||||||
|
|
||||||
<h1>Attribution (détails)</h1>
|
|
||||||
<h2>Token : {{ token }}</h2>
|
|
||||||
<h2>Placés : {{ total_slots }} ; Déçus : {{ total_losers }}</h2>
|
|
||||||
|
|
||||||
<table>
|
|
||||||
{% for member, shows in members2 %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ member.user.get_full_name }}</td>
|
|
||||||
<td>{{ member.user.email }}</td>
|
|
||||||
<td>Total: {{ member.total }}€</td>
|
|
||||||
<td style="width: 120px;"></td>
|
|
||||||
</tr>
|
|
||||||
{% for show in shows %}
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td>{{ show }}</td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
{% endblock %}
|
|
|
@ -1,41 +0,0 @@
|
||||||
{% extends "base_title.html" %}
|
|
||||||
{% load staticfiles %}
|
|
||||||
|
|
||||||
{% block extra_head %}
|
|
||||||
<link type="text/css" rel="stylesheet" href="{% static "css/bda.css" %}" />
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block realcontent %}
|
|
||||||
|
|
||||||
<h1>Attribution</h1>
|
|
||||||
<h2>Token : {{ token }}</h2>
|
|
||||||
<h2>Placés : {{ total_slots }} ; Déçus : {{ total_losers }}</h2>
|
|
||||||
{% if user.profile.is_buro %}<h2>Déficit total: {{ total_deficit }} €, Opéra: {{ opera_deficit }} €, Attribué: {{ total_sold }} €</h2>{% endif %}
|
|
||||||
|
|
||||||
{% for show, members, losers in results %}
|
|
||||||
<div class="attribresult">
|
|
||||||
<h2>{{ show.title }} - {{ show.date_no_seconds }} @ {{ show.location }}</h2>
|
|
||||||
<p>
|
|
||||||
<strong>{{ show.nrequests }} demandes pour {{ show.slots }} places</strong>
|
|
||||||
{{ show.price }}€ par place{% if user.profile.is_buro and show.nrequests < show.slots %}, {{ show.deficit }}€ de déficit{% endif %}
|
|
||||||
</p>
|
|
||||||
Places :
|
|
||||||
<ul>
|
|
||||||
{% for member, rank, origrank, double in members %}
|
|
||||||
<li>{{ member.user.get_full_name }} <span class="details">(souhait {{ origrank }} — rang {{ rank }})</span></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
Déçus :
|
|
||||||
{% if not losers %}/{% else %}
|
|
||||||
<ul class="losers">
|
|
||||||
{% for member, rank, origrank, double in losers %}
|
|
||||||
{% if not forloop.first %} ; {% endif %}
|
|
||||||
<li>{{ member.user.get_full_name }} <span class="details">(souhait {{ origrank }} — rang {{ rank }})</span></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% block extracontent %}
|
|
||||||
{% endblock %}
|
|
||||||
{% endblock %}
|
|
|
@ -1,7 +0,0 @@
|
||||||
{% extends "base_title.html" %}
|
|
||||||
|
|
||||||
{% block realcontent %}
|
|
||||||
<h2>{{ spectacle }}</h2>
|
|
||||||
<textarea style="width: 100%; height: 100px; margin-top: 10px;">
|
|
||||||
{% for attrib in spectacle.attribues.all %}{{ attrib.participant.user.email }}, {% endfor %}</textarea>
|
|
||||||
{% endblock %}
|
|
|
@ -1,6 +0,0 @@
|
||||||
{% extends "base_title.html" %}
|
|
||||||
|
|
||||||
{% block realcontent %}
|
|
||||||
<h1><strong>Nope</strong></h1>
|
|
||||||
<p>Avant de revendre des places, il faut aller les payer !</p>
|
|
||||||
{% endblock %}
|
|
|
@ -1,26 +0,0 @@
|
||||||
{% extends "base_title.html" %}
|
|
||||||
{% load staticfiles %}
|
|
||||||
|
|
||||||
{% block extra_head %}
|
|
||||||
<link type="text/css" rel="stylesheet" href="{% static "css/bda.css" %}" />
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block realcontent %}
|
|
||||||
|
|
||||||
<h1>Revente de place</h1>
|
|
||||||
<form action="" method="post" id="resellform">
|
|
||||||
{% csrf_token %}
|
|
||||||
{% if form.spectacle.errors %}<ul class="errorlist"><li>Sélectionnez un spetacle</li></ul>{% endif %}
|
|
||||||
<p>
|
|
||||||
<code>
|
|
||||||
Bonjour,<br />
|
|
||||||
<br />
|
|
||||||
Je souhaite revendre {{ form.count }} place(s) pour {{ form.spectacle }}.<br />
|
|
||||||
Contactez-moi par email si vous êtes intéressé !<br />
|
|
||||||
<br />
|
|
||||||
{{ user.get_full_name }} ({{ user.email }})
|
|
||||||
</code>
|
|
||||||
</p>
|
|
||||||
<input type="submit" value="Envoyer" />
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
|
|
@ -1,12 +0,0 @@
|
||||||
{% extends "base_title.html" %}
|
|
||||||
{% load staticfiles %}
|
|
||||||
|
|
||||||
{% block extra_head %}
|
|
||||||
<link type="text/css" rel="stylesheet" href="{% static "css/bda.css" %}" />
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block realcontent %}
|
|
||||||
|
|
||||||
<h1>Revente de place</h1>
|
|
||||||
<p class="success">Votre offre de revente de {{ places }} pour {{ show.title }} le {{ show.date_no_seconds }} ({{ show.location }}) à {{ show.price }}€ a bien été envoyée.</p>
|
|
||||||
{% endblock %}
|
|
|
@ -1,13 +0,0 @@
|
||||||
{% extends "base_title.html" %}
|
|
||||||
|
|
||||||
{% block realcontent %}
|
|
||||||
<h2>Tirage au sort du BdA</h2>
|
|
||||||
<form action="" method="post" id="tokenform">
|
|
||||||
{% csrf_token %}
|
|
||||||
<strong>La graine :</strong>
|
|
||||||
<div>
|
|
||||||
{{ form.token }}
|
|
||||||
</div>
|
|
||||||
<input type="submit" value="Go" />
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
|
|
@ -1,7 +0,0 @@
|
||||||
{% extends "base_title.html" %}
|
|
||||||
|
|
||||||
{% block realcontent %}
|
|
||||||
<h2>Impayés</h2>
|
|
||||||
<textarea style="width: 100%; height: 100px; margin-top: 10px;">
|
|
||||||
{% for participant in unpaid %}{{ participant.user.email }}, {% endfor %}</textarea>
|
|
||||||
{% endblock %}
|
|
|
@ -1,11 +0,0 @@
|
||||||
{% extends "base_title.html" %}
|
|
||||||
|
|
||||||
{% block realcontent %}
|
|
||||||
<h2>Etat des inscriptions BDA</h2>
|
|
||||||
<ul>
|
|
||||||
{% for spectacle in spectacles %}
|
|
||||||
<li>{{ spectacle.title }} (<span style="font-size: 0.5em;">{{ spectacle.date_no_seconds }}, {{ spectacle.location }}</span>, {{ spectacle.slots }} places) : <strong>{{ spectacle.total }} demandes</strong></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
<strong>Total : <u>{{ total }} demandes</u></strong>
|
|
||||||
{% endblock %}
|
|
|
@ -1,116 +0,0 @@
|
||||||
{% extends "base_title.html" %}
|
|
||||||
|
|
||||||
{% block extra_head %}
|
|
||||||
<link href="{{ STATIC_URL }}grappelli/jquery/ui/css/custom-theme/jquery-ui-1.8.custom.css" rel="stylesheet" type="text/css" media="screen" title="no title" charset="utf-8" />
|
|
||||||
<script src="{{ STATIC_URL }}grappelli/jquery/jquery-1.6.2.min.js" type="text/javascript"></script>
|
|
||||||
<script src="{{ STATIC_URL }}grappelli/jquery/ui/js/jquery-ui-1.8.15.custom.min.js" type="text/javascript"></script>
|
|
||||||
<link href="{{ STATIC_URL }}grappelli/css/tools.css" rel="stylesheet" type="text/css" />
|
|
||||||
<link href="{{ STATIC_URL }}grappelli/css/jquery-ui-grappelli-extensions.css" rel="stylesheet" type="text/css" />
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block realcontent %}
|
|
||||||
<script type="text/javascript">
|
|
||||||
var django = {
|
|
||||||
"jQuery": jQuery.noConflict(true)
|
|
||||||
};
|
|
||||||
|
|
||||||
(function($) {
|
|
||||||
cloneMore = function(selector, type) {
|
|
||||||
var newElement = $(selector).clone(true);
|
|
||||||
var total = $('#id_' + type + '-TOTAL_FORMS').val();
|
|
||||||
newElement.find(':input').each(function() {
|
|
||||||
var name = $(this).attr('name').replace('-' + (total-1) + '-','-' + total + '-');
|
|
||||||
var id = 'id_' + name;
|
|
||||||
$(this).attr({'name': name, 'id': id}).val('').removeAttr('checked');
|
|
||||||
});
|
|
||||||
newElement.find('label').each(function() {
|
|
||||||
var newFor = $(this).attr('for').replace('-' + (total-1) + '-','-' + total + '-');
|
|
||||||
$(this).attr('for', newFor);
|
|
||||||
});
|
|
||||||
total++;
|
|
||||||
$('#id_' + type + '-TOTAL_FORMS').val(total);
|
|
||||||
$(selector).after(newElement);
|
|
||||||
}
|
|
||||||
deleteButtonHandler = function(elem) {
|
|
||||||
elem.bind("click", function() {
|
|
||||||
var deleteInput = $(this).prev().prev(),
|
|
||||||
form = $(this).parents(".dynamic-form").first();
|
|
||||||
// callback
|
|
||||||
// toggle options.predeleteCssClass and toggle checkbox
|
|
||||||
if (form.hasClass("has_original")) {
|
|
||||||
form.toggleClass("predelete");
|
|
||||||
if (deleteInput.attr("checked")) {
|
|
||||||
deleteInput.attr("checked", false);
|
|
||||||
} else {
|
|
||||||
deleteInput.attr("checked", true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// callback
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$(document).ready(function($) {
|
|
||||||
deleteButtonHandler($("table#bda_formset tbody.bda_formset_content").find("a.delete-handler"));
|
|
||||||
$("table#bda_formset tbody.bda_formset_content").sortable({
|
|
||||||
handle: "a.drag-handler",
|
|
||||||
items: "tr",
|
|
||||||
axis: "y",
|
|
||||||
appendTo: 'body',
|
|
||||||
forceHelperSize: true,
|
|
||||||
placeholder: 'ui-sortable-placeholder',
|
|
||||||
forcePlaceholderSize: true,
|
|
||||||
containment: 'form#bda_form',
|
|
||||||
tolerance: 'pointer',
|
|
||||||
start: function(evt, ui) {
|
|
||||||
var template = "",
|
|
||||||
len = ui.item.children("td").length;
|
|
||||||
for (var i = 0; i < len; i++) {
|
|
||||||
template += "<td style='height:" + (ui.item.outerHeight() + 12 ) + "px' class='placeholder-cell'> </td>"
|
|
||||||
}
|
|
||||||
template += "";
|
|
||||||
ui.placeholder.html(template);
|
|
||||||
},
|
|
||||||
stop: function(evt, ui) {
|
|
||||||
// Toggle div.table twice to remove webkits border-spacing bug
|
|
||||||
$("table#bda_formset").toggle().toggle();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
$("#bda_form").bind("submit", function(){
|
|
||||||
var sortable_field_name = "priority";
|
|
||||||
var i = 1;
|
|
||||||
$(".bda_formset_content").find("tr").each(function(){
|
|
||||||
var fields = $(this).find("td :input[value]"),
|
|
||||||
select = $(this).find("td select");
|
|
||||||
if (select.val() && fields.serialize()) {
|
|
||||||
$(this).find("input[name$='"+sortable_field_name+"']").val(i);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
})(django.jQuery);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<h2>Inscription au tirage au sort du BDA</h2>
|
|
||||||
{% if success %}
|
|
||||||
<p class="success">Votre inscription a été mise à jour avec succès !</p>
|
|
||||||
{% endif %}
|
|
||||||
<form id="bda_form" method="post" action="{% url 'bda-tirage-inscription' %}">
|
|
||||||
{% csrf_token %}
|
|
||||||
{% include "inscription-formset.html" %}
|
|
||||||
<input type="button" class="btn-addmore" value="Ajouter un autre vœu" id="add_more">
|
|
||||||
<script>
|
|
||||||
django.jQuery('#add_more').click(function() {
|
|
||||||
cloneMore('tbody.bda_formset_content tr:last-child', 'choixspectacle_set');
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<input type="submit" class="btn-submit" value="Enregistrer" />
|
|
||||||
Prix total actuel : {{ total_price }}€
|
|
||||||
<hr />
|
|
||||||
<p class="footnotes">
|
|
||||||
<sup>1</sup>: demander deux places pour ce spectable<br />
|
|
||||||
<sup>2</sup>: abandonner une place si impossible d'en obtenir une seconde pour ce spectacle (si vous avez coché l'option <tt>Deux places</tt> pour ce spectacle)<br />
|
|
||||||
<sup>3</sup>: 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<br />
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
|
|
@ -1,40 +0,0 @@
|
||||||
{{ formset.non_form_errors.as_ul }}
|
|
||||||
<table id="bda_formset" class="form">
|
|
||||||
{{ formset.management_form }}
|
|
||||||
{% for form in formset.forms %}
|
|
||||||
{% if forloop.first %}
|
|
||||||
<thead><tr>
|
|
||||||
{% for field in form.visible_fields %}
|
|
||||||
{% if field.name != "DELETE" and field.name != "priority" %}
|
|
||||||
<th class="bda-field-{{ field.name }}">{{ field.label|safe|capfirst }}</th>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
<th><sup>3</sup></th>
|
|
||||||
</tr></thead>
|
|
||||||
<tbody class="bda_formset_content">
|
|
||||||
{% endif %}
|
|
||||||
<tr class="{% cycle row1,row2 %} dynamic-form {% if form.instance.pk %}has_original{% endif %}">
|
|
||||||
{% for field in form.visible_fields %}
|
|
||||||
{% if field.name != "DELETE" and field.name != "priority" %}
|
|
||||||
<td class="bda-field-{{ field.name }}">
|
|
||||||
{% if forloop.first %}
|
|
||||||
{{ form.non_field_errors }}
|
|
||||||
{% for hidden in form.hidden_fields %}{{ hidden }}{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{{ field.errors.as_ul }}
|
|
||||||
{{ field }}
|
|
||||||
</td>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
<td class="tools-cell"><div class="tools">
|
|
||||||
<a href="javascript://" class="icon drag-handler" title="Déplacer"></a>
|
|
||||||
<input type="checkbox" name="{{ form.DELETE.html_name }}" style="display: none;" />
|
|
||||||
<input type="hidden" name="{{ form.priority.html_name }}" style="{{ form.priority.value }}" />
|
|
||||||
<a href="javascript://" class="icon delete-handler" title="Supprimer"></a>
|
|
||||||
</div>
|
|
||||||
<div class="spacer"></div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
|
@ -1,11 +0,0 @@
|
||||||
{% extends "base_title.html" %}
|
|
||||||
|
|
||||||
{% block realcontent %}
|
|
||||||
<h1><strong>Spectacles</strong></h1>
|
|
||||||
<ul>
|
|
||||||
<li><a href="{% url 'bda2-unpaid' %}">Pas payé</a></li>
|
|
||||||
{% for spectacle in object_list %}
|
|
||||||
<li><a href="{% url 'bda2-spectacle' spectacle.id %}">{{ spectacle }}</a></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% endblock %}
|
|
|
@ -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)
|
|
214
bda2/views.py
214
bda2/views.py
|
@ -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()})
|
|
180
bda3/admin.py
180
bda3/admin.py
|
@ -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)
|
|
|
@ -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
|
|
|
@ -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...'})
|
|
421
bda3/difftobda
421
bda3/difftobda
|
@ -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~
|
|
|
@ -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()
|
|
||||||
|
|
|
@ -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 places<sup>1</sup>')),
|
|
||||||
('autoquit', models.BooleanField(default=False, verbose_name=b'Abandon<sup>2</sup>')),
|
|
||||||
],
|
|
||||||
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')]),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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 places<sup>1</sup>",default=False)
|
|
||||||
autoquit = models.BooleanField("Abandon<sup>2</sup>",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)
|
|
|
@ -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;}
|
|
|
@ -1,11 +0,0 @@
|
||||||
{% extends "base_title.html" %}
|
|
||||||
|
|
||||||
{% block realcontent %}
|
|
||||||
<h1><strong>Spectacles</strong></h1>
|
|
||||||
<ul>
|
|
||||||
<li><a href="{% url 'bda3-unpaid' %}">Pas payé</a></li>
|
|
||||||
{% for spectacle in object_list %}
|
|
||||||
<li><a href="{% url 'bda3-spectacle' spectacle.id %}">{{ spectacle }}</a></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% endblock %}
|
|
|
@ -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)
|
|
215
bda3/views.py
215
bda3/views.py
|
@ -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()})
|
|
|
@ -41,8 +41,6 @@ INSTALLED_APPS = (
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
'django.contrib.admindocs',
|
'django.contrib.admindocs',
|
||||||
'bda',
|
'bda',
|
||||||
'bda2',
|
|
||||||
'bda3',
|
|
||||||
'autocomplete_light',
|
'autocomplete_light',
|
||||||
'captcha',
|
'captcha',
|
||||||
'django_cas_ng',
|
'django_cas_ng',
|
||||||
|
|
40
cof/urls.py
40
cof/urls.py
|
@ -6,11 +6,9 @@ autocomplete_light.autodiscover()
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
admin.autodiscover()
|
admin.autodiscover()
|
||||||
|
|
||||||
from django.views.generic.list import ListView
|
|
||||||
from django.views.generic.base import TemplateView
|
from django.views.generic.base import TemplateView
|
||||||
from bda.models import Spectacle
|
from bda.models import Spectacle
|
||||||
from bda2.models import Spectacle as Spectacle2
|
from bda.views import SpectacleListView
|
||||||
from bda3.models import Spectacle as Spectacle3
|
|
||||||
from gestioncof.petits_cours_views import DemandeListView
|
from gestioncof.petits_cours_views import DemandeListView
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
|
@ -42,32 +40,16 @@ urlpatterns = patterns('',
|
||||||
url(r'^petitcours/demandes/(?P<demande_id>\d+)$', 'gestioncof.petits_cours_views.details', name = 'petits-cours-demande-details'),
|
url(r'^petitcours/demandes/(?P<demande_id>\d+)$', 'gestioncof.petits_cours_views.details', name = 'petits-cours-demande-details'),
|
||||||
url(r'^petitcours/demandes/(?P<demande_id>\d+)/traitement$', 'gestioncof.petits_cours_views.traitement', name = 'petits-cours-demande-traitement'),
|
url(r'^petitcours/demandes/(?P<demande_id>\d+)/traitement$', 'gestioncof.petits_cours_views.traitement', name = 'petits-cours-demande-traitement'),
|
||||||
url(r'^petitcours/demandes/(?P<demande_id>\d+)/retraitement$', 'gestioncof.petits_cours_views.retraitement', name = 'petits-cours-demande-retraitement'),
|
url(r'^petitcours/demandes/(?P<demande_id>\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'^bda/inscription/(?P<tirage_id>\d+)$', 'bda.views.inscription', name = 'bda-tirage-inscription'),
|
||||||
url(r'^bda2/inscription$', 'bda2.views.inscription', name = 'bda2-tirage-inscription'),
|
url(r'^bda/places/(?P<tirage_id>\d+)$', 'bda.views.places', name = "bda-places-attribuees"),
|
||||||
url(r'^bda3/inscription$', 'bda3.views.inscription', name = 'bda3-tirage-inscription'),
|
url(r'^bda/places/(?P<tirage_id>\d+)/places_bda.ics$', 'bda.views.places_ics', name = "bda-places-attribuees-ics"),
|
||||||
url(r'^bda/places$', 'bda.views.places', name = "bda-places-attribuees"),
|
url(r'^bda/revente/(?P<tirage_id>\d+)$', 'bda.views.revente', name = 'bda-revente'),
|
||||||
url(r'^bda/places/places_bda.ics$', 'bda.views.places_ics', name = "bda-places-attribuees-ics"),
|
url(r'^bda/etat-places/(?P<tirage_id>\d+)$', 'bda.views.etat_places', name = 'bda-etat-places'),
|
||||||
url(r'^bda2/places$', 'bda2.views.places', name = "bda2-places-attribuees"),
|
url(r'^bda/tirage/(?P<tirage_id>\d+)$', 'bda.views.tirage'),
|
||||||
url(r'^bda3/places$', 'bda3.views.places', name = "bda3-places-attribuees"),
|
url(r'^bda/spectacles/(?P<tirage_id>\d+)$', SpectacleListView.as_view() , name ="bda-liste-spectacles"),
|
||||||
url(r'^bda/revente$', 'bda.views.revente', name = 'bda-revente'),
|
url(r'^bda/spectacles/(?P<tirage_id>\d+)/(?P<spectacle_id>\d+)$', "bda.views.spectacle", name = "bda-spectacle"),
|
||||||
url(r'^bda2/revente$', 'bda2.views.revente', name = 'bda2-revente'),
|
url(r'^bda/spectacles-ics/(?P<tirage_id>\d+)$', 'bda.views.liste_spectacles_ics', name ="bda-liste-spectacles-ics"),
|
||||||
url(r'^bda3/revente$', 'bda3.views.revente', name = 'bda3-revente'),
|
url(r'^bda/spectacles/unpaid/(?P<tirage_id>\d+)$', "bda.views.unpaid", name = "bda-unpaid"),
|
||||||
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<spectacle_id>\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<spectacle_id>\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<spectacle_id>\d+)$', "bda3.views.spectacle", name = "bda3-spectacle"),
|
|
||||||
url(r'^survey/(?P<survey_id>\d+)$', 'gestioncof.views.survey'),
|
url(r'^survey/(?P<survey_id>\d+)$', 'gestioncof.views.survey'),
|
||||||
url(r'^event/(?P<event_id>\d+)$', 'gestioncof.views.event'),
|
url(r'^event/(?P<event_id>\d+)$', 'gestioncof.views.event'),
|
||||||
url(r'^survey/(?P<survey_id>\d+)/status$', 'gestioncof.views.survey_status'),
|
url(r'^survey/(?P<survey_id>\d+)/status$', 'gestioncof.views.survey_status'),
|
||||||
|
|
|
@ -24,29 +24,19 @@
|
||||||
|
|
||||||
|
|
||||||
{% if user.profile.is_cof %}
|
{% if user.profile.is_cof %}
|
||||||
<h3>BdA</h3>
|
{% if open_tirages %}
|
||||||
|
<h3>Tirages du BdA</h3>
|
||||||
|
{% for tirage in open_tirages %}
|
||||||
<ul>
|
<ul>
|
||||||
<!-- <li><a href="{% url "bda-tirage-inscription" %}">Inscription au premier tirage au sort du BdA</a></li>
|
{{ tirage.title }}
|
||||||
<li><a href="{% url "bda-etat-places" %}">État des demandes</a>-->
|
<li><a href="{% url "bda-tirage-inscription" tirage.id %}">Inscription</a></li>
|
||||||
<li><a href="{% url "bda-places-attribuees" %}">Mes places du premier tirage</a></li>
|
<li><a href="{% url "bda-etat-places" tirage.id %}">État des demandes</a>
|
||||||
<li><a href="{% url "bda-revente" %}">Revendre une place du premier tirage</a></li>
|
<li><a href="{% url "bda-places-attribuees" tirage.id %}">Mes places</a></li>
|
||||||
|
<li><a href="{% url "bda-revente" tirage.id %}">Revendre une place</a></li>
|
||||||
<br>
|
<br>
|
||||||
Second tirage
|
|
||||||
<!-- <li><a href="{% url "bda2-tirage-inscription" %}">Inscription au deuxième tirage au sort du BdA</a></li>
|
|
||||||
<li><a href="{% url "bda2-etat-places" %}">État des demandes</a></li> -->
|
|
||||||
<li><a href="{% url "bda2-places-attribuees" %}">Mes places du deuxième tirage</a></li>
|
|
||||||
<li><a href="{% url "bda2-revente" %}">Revendre une place du deuxième tirage</a></li>
|
|
||||||
<br>
|
|
||||||
|
|
||||||
|
|
||||||
Troisième tirage
|
|
||||||
<!--<li><a href="{% url "bda3-tirage-inscription" %}">Inscription au troisième tirage au sort du BdA</a></li>
|
|
||||||
<li><a href="{% url "bda3-etat-places" %}">État des demandes</a></li>-->
|
|
||||||
<li><a href="{% url "bda3-places-attribuees" %}">Mes places du troisième tirage</a></li>
|
|
||||||
<li><a href="{% url "bda3-revente" %}">Revendre une place du troisième tirage</a></li>
|
|
||||||
<br>
|
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<h3>Divers</h3>
|
<h3>Divers</h3>
|
||||||
|
|
|
@ -10,23 +10,14 @@
|
||||||
<li><a href="{% url 'gestioncof.views.liste_bdadiff' %}">BdA diffusion</a></li>
|
<li><a href="{% url 'gestioncof.views.liste_bdadiff' %}">BdA diffusion</a></li>
|
||||||
<li><a href="{% url 'gestioncof.views.liste_bdarevente' %}">BdA revente</a></li>
|
<li><a href="{% url 'gestioncof.views.liste_bdarevente' %}">BdA revente</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<h3>Premier tirage</h3>
|
<h3>Tirages</h3>
|
||||||
|
{% for tirage in tirages %}
|
||||||
|
<h4>{{ tirage.title }}</h4>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="{% url 'bda.views.etat_places' %}">Etat des voeux</a></li>
|
<li><a href="{% url 'bda.views.etat_places' tirage.id %}">Etat des voeux</a></li>
|
||||||
<li><a href="{% url 'bda-liste-spectacles' %}">Mailing list par spectacle</a></li>
|
<li><a href="{% url 'bda-liste-spectacles' tirage.id %}">Mailing list par spectacle</a></li>
|
||||||
<li><a href="{% url 'bda.views.unpaid' %}">Mailing list des impayés</a></li>
|
<li><a href="{% url 'bda.views.unpaid' tirage.id %}">Mailing list des impayés</a></li>
|
||||||
<li><a href="{% url 'bda-liste-spectacles-ics' %}">Calendrier des spectacles (.ics)</a></li>
|
<li><a href="{% url 'bda-liste-spectacles-ics' tirage.id %}">Calendrier des spectacles (.ics)</a></li>
|
||||||
</ul>
|
|
||||||
<h3>Deuxième tirage</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="{% url 'bda2.views.etat_places' %}">Etat des voeux</a></li>
|
|
||||||
<li><a href="{% url 'bda2-liste-spectacles' %}">Mailing list par spectacle</a></li>
|
|
||||||
<li><a href="{% url 'bda2.views.unpaid' %}">Mailing list des impayés</a></li>
|
|
||||||
</ul>
|
|
||||||
<h3>Troisième tirage</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="{% url 'bda3.views.etat_places' %}">Etat des voeux</a></li>
|
|
||||||
<li><a href="{% url 'bda3-liste-spectacles' %}">Mailing list par spectacle</a></li>
|
|
||||||
<li><a href="{% url 'bda3.views.unpaid' %}">Mailing list des impayés</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
{% endfor %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -20,6 +20,8 @@ from gestioncof.forms import UserProfileForm, EventStatusFilterForm, \
|
||||||
SurveyForm, SurveyStatusFilterForm, RegistrationUserForm, \
|
SurveyForm, SurveyStatusFilterForm, RegistrationUserForm, \
|
||||||
RegistrationProfileForm, AdminEventForm, EventForm
|
RegistrationProfileForm, AdminEventForm, EventForm
|
||||||
|
|
||||||
|
from bda.models import Tirage
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -27,7 +29,8 @@ def home(request):
|
||||||
data = {"surveys": Survey.objects.filter(old=False).all(),
|
data = {"surveys": Survey.objects.filter(old=False).all(),
|
||||||
"events": Event.objects.filter(old=False).all(),
|
"events": Event.objects.filter(old=False).all(),
|
||||||
"open_surveys": Survey.objects.filter(survey_open=True, 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_events": Event.objects.filter(registration_open=True, old=False).all(),
|
||||||
|
"open_tirages": Tirage.objects.filter(active=True).all()}
|
||||||
return render(request, "home.html", data)
|
return render(request, "home.html", data)
|
||||||
|
|
||||||
def login(request):
|
def login(request):
|
||||||
|
@ -496,7 +499,8 @@ def utile_cof(request):
|
||||||
|
|
||||||
@buro_required
|
@buro_required
|
||||||
def utile_bda(request):
|
def utile_bda(request):
|
||||||
return render(request, "utile_bda.html", {})
|
tirages = Tirage.objects.all()
|
||||||
|
return render(request, "utile_bda.html", {'tirages': tirages})
|
||||||
|
|
||||||
@buro_required
|
@buro_required
|
||||||
def liste_bdadiff(request):
|
def liste_bdadiff(request):
|
||||||
|
|
Loading…
Reference in a new issue