Suppression des vieux dossiers
This commit is contained in:
parent
ac0346d69f
commit
7c7488f168
35 changed files with 0 additions and 2676 deletions
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,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)
|
|
248
bda2/views.py
248
bda2/views.py
|
@ -1,248 +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 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 bda2.models import Spectacle, Participant, ChoixSpectacle, Attribution
|
|
||||||
from bda2.algorithm import Algorithm
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
@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)
|
|
||||||
|
|
||||||
class TokenForm(forms.Form):
|
|
||||||
token = forms.CharField(widget = forms.widgets.Textarea())
|
|
||||||
|
|
||||||
@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})
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
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,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)
|
|
249
bda3/views.py
249
bda3/views.py
|
@ -1,249 +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 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 bda3.models import Spectacle, Participant, ChoixSpectacle, Attribution
|
|
||||||
from bda3.algorithm import Algorithm
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
@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)
|
|
||||||
|
|
||||||
class TokenForm(forms.Form):
|
|
||||||
token = forms.CharField(widget = forms.widgets.Textarea())
|
|
||||||
|
|
||||||
@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})
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
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()})
|
|
Loading…
Reference in a new issue