2012-07-11 17:39:20 +02:00
# coding: utf-8
2014-08-19 12:54:22 +02:00
from django . contrib . auth . models import User
2013-09-05 22:20:52 +02:00
from django . shortcuts import render , get_object_or_404
2012-07-11 17:39:20 +02:00
from django . contrib . auth . decorators import login_required
2013-09-05 22:20:52 +02:00
from django . db import models
from django . http import Http404
2012-07-11 17:39:20 +02:00
from django import forms
from django . forms . models import inlineformset_factory , BaseInlineFormSet
2014-08-19 12:54:22 +02:00
from django . core import serializers
import hashlib
2012-07-11 17:39:20 +02:00
2013-09-05 22:20:52 +02:00
from django . core . mail import send_mail
2016-05-21 23:57:36 +02:00
from datetime import datetime , timedelta
2013-09-05 22:20:52 +02:00
import time
from gestioncof . decorators import cof_required , buro_required
2014-08-19 12:54:22 +02:00
from gestioncof . shared import send_custom_mail
2013-09-05 22:20:52 +02:00
from bda . models import Spectacle , Participant , ChoixSpectacle , Attribution
from bda . algorithm import Algorithm
2012-07-11 17:39:20 +02:00
class BaseBdaFormSet ( BaseInlineFormSet ) :
def clean ( self ) :
""" Checks that no two articles have the same title. """
2013-09-05 22:20:52 +02:00
super ( BaseBdaFormSet , self ) . clean ( )
2012-07-11 17:39:20 +02:00
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 )
2013-10-01 15:27:19 +02:00
@cof_required
2013-09-05 22:20:52 +02:00
def etat_places ( request ) :
2015-09-13 18:23:47 +02:00
spectacles1 = ChoixSpectacle . objects . filter ( double_choice = " 1 " ) . all ( ) . values ( ' spectacle ' , ' spectacle__title ' ) . annotate ( total = models . Count ( ' spectacle ' ) )
spectacles2 = ChoixSpectacle . objects . exclude ( double_choice = " 1 " ) . all ( ) . values ( ' spectacle ' , ' spectacle__title ' ) . annotate ( total = models . Count ( ' spectacle ' ) )
2013-09-05 22:20:52 +02:00
spectacles = Spectacle . objects . all ( )
spectacles_dict = { }
total = 0
for spectacle in spectacles :
spectacle . total = 0
2015-01-06 11:01:15 +01:00
spectacle . ratio = 0.0
2013-09-05 22:20:52 +02:00
spectacles_dict [ spectacle . id ] = spectacle
for spectacle in spectacles1 :
spectacles_dict [ spectacle [ " spectacle " ] ] . total + = spectacle [ " total " ]
2014-08-19 12:54:22 +02:00
spectacles_dict [ spectacle [ " spectacle " ] ] . ratio = spectacles_dict [ spectacle [ " spectacle " ] ] . total / float ( spectacles_dict [ spectacle [ " spectacle " ] ] . slots )
2013-09-05 22:20:52 +02:00
total + = spectacle [ " total " ]
for spectacle in spectacles2 :
2016-05-21 23:57:36 +02:00
spectacles_dict [ spectacle [ " spectacle " ] ] . total + = 2 * spectacle [ " total " ]
2014-08-19 12:54:22 +02:00
spectacles_dict [ spectacle [ " spectacle " ] ] . ratio = spectacles_dict [ spectacle [ " spectacle " ] ] . total / float ( spectacles_dict [ spectacle [ " spectacle " ] ] . slots )
2013-09-05 22:20:52 +02:00
total + = spectacle [ " total " ]
return render ( request , " etat-places.html " , { " spectacles " : spectacles , " total " : total } )
2014-08-19 12:54:22 +02:00
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 } )
2016-05-21 23:57:36 +02:00
@cof_required
def places_ics ( request ) :
participant , created = Participant . objects . get_or_create ( user = request . user )
places = participant . attribution_set . order_by ( " spectacle__date " , " spectacle " ) . all ( )
filtered_places = [ ]
places_dict = { }
spectacles = [ ]
for place in places :
if place . spectacle in spectacles :
places_dict [ place . spectacle ] . double = True
else :
place . double = False
place . spectacle . dtend = place . spectacle . date + timedelta ( seconds = 7200 )
places_dict [ place . spectacle ] = place
spectacles . append ( place . spectacle )
filtered_places . append ( place )
date = place . spectacle . date . date ( )
return render ( request , " resume_places.ics " ,
{ " participant " : participant ,
" places " : filtered_places } , content_type = " text/calendar " )
2013-09-05 22:20:52 +02:00
@cof_required
2012-07-11 17:39:20 +02:00
def inscription ( request ) :
2016-05-21 23:57:36 +02:00
if datetime . now ( ) > datetime ( 2015 , 10 , 4 , 12 , 00 ) and request . user . username != " seguin " :
2014-08-19 12:54:22 +02:00
participant , created = Participant . objects . get_or_create ( user = request . user )
choices = participant . choixspectacle_set . order_by ( " priority " ) . all ( )
2015-01-06 11:01:15 +01:00
return render ( request , " resume_inscription.html " , { " error_title " : " C ' est fini ! " , " error_description " : u " Tirage au sort dans la journée ! " , " choices " : choices } )
2015-09-13 18:23:47 +02:00
BdaFormSet = inlineformset_factory ( Participant , ChoixSpectacle , fields = ( " spectacle " , " double_choice " , " priority " , ) , formset = BaseBdaFormSet )
2012-07-11 17:39:20 +02:00
participant , created = Participant . objects . get_or_create ( user = request . user )
success = False
2014-08-19 12:54:22 +02:00
stateerror = False
2012-07-11 17:39:20 +02:00
if request . method == " POST " :
2014-08-19 12:54:22 +02:00
dbstate = _hash_queryset ( participant . choixspectacle_set . all ( ) )
if " dbstate " in request . POST and dbstate != request . POST [ " dbstate " ] :
stateerror = True
2012-07-11 17:39:20 +02:00
formset = BdaFormSet ( instance = participant )
2014-08-19 12:54:22 +02:00
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 )
2012-07-11 17:39:20 +02:00
else :
formset = BdaFormSet ( instance = participant )
2014-08-19 12:54:22 +02:00
dbstate = _hash_queryset ( participant . choixspectacle_set . all ( ) )
2013-09-05 22:20:52 +02:00
total_price = 0
for choice in participant . choixspectacle_set . all ( ) :
total_price + = choice . spectacle . price
if choice . double : total_price + = choice . spectacle . price
2014-08-19 12:54:22 +02:00
return render ( request , " inscription-bda.html " , { " formset " : formset , " success " : success , " total_price " : total_price , " dbstate " : dbstate , " stateerror " : stateerror } )
2013-09-05 22:20:52 +02:00
def do_tirage ( request ) :
form = TokenForm ( request . POST )
if not form . is_valid ( ) :
return tirage ( request )
2014-08-19 12:54:22 +02:00
start = time . time ( )
2013-09-05 22:20:52 +02:00
data = { }
2014-08-19 12:54:22 +02:00
shows = Spectacle . objects . select_related ( ) . all ( )
2013-09-05 22:20:52 +02:00
members = Participant . objects . all ( )
2014-08-19 12:54:22 +02:00
choices = ChoixSpectacle . objects . order_by ( ' participant ' , ' priority ' ) . select_related ( ) . all ( )
2013-09-05 22:20:52 +02:00
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
2014-08-19 12:54:22 +02:00
for ( show , members , _ ) in results :
deficit = ( show . slots - len ( members ) ) * show . price
2013-09-05 22:20:52 +02:00
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
2014-08-19 12:54:22 +02:00
data [ " duration " ] = time . time ( ) - start
2013-09-05 22:20:52 +02:00
if request . user . is_authenticated ( ) :
members2 = { }
2014-08-19 12:54:22 +02:00
members_uniq = { } # Participant objects are not shared accross spectacle results,
# So assign a single object for each Participant id
2013-09-05 22:20:52 +02:00
for ( show , members , _ ) in results :
for ( member , _ , _ , _ ) in members :
2014-08-19 12:54:22 +02:00
if member . id not in members_uniq :
members_uniq [ member . id ] = member
2013-09-05 22:20:52 +02:00
members2 [ member ] = [ ]
member . total = 0
2014-08-19 12:54:22 +02:00
member = members_uniq [ member . id ]
2013-09-05 22:20:52 +02:00
members2 [ member ] . append ( show )
member . total + = show . price
members2 = members2 . items ( )
data [ " members2 " ] = sorted ( members2 , key = lambda m : m [ 0 ] . user . last_name )
2016-05-21 23:57:36 +02:00
if False and request . user . username in [ " seguin " , " harazi " , " fromherz " , " ccadiou " ] :
2013-09-05 22:20:52 +02:00
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 "
2014-08-19 12:54:22 +02:00
"""
send_custom_mail ( " bda-revente@lists.ens.fr " ,
" bda-revente " ,
{ " places " : places ,
" spectacle " : spectacle . title ,
" date " : spectacle . date_no_seconds ( ) ,
" lieu " : spectacle . location ,
" prix " : spectacle . price ,
" revendeur " : request . user . get_full_name ( ) ,
" revendeur_mail " : request . user . email } ,
from_email = request . user . email )
"""
2013-09-05 22:20:52 +02:00
mail = u """ Bonjour,
Je souhaite revendre % s pour % s le % s ( % s ) à % .02 f € .
2014-08-19 12:54:22 +02:00
Contactez moi par email si vous êtes intéressé · e · s !
2013-09-05 22:20:52 +02:00
% s ( % s ) """ % (places, spectacle.title, spectacle.date_no_seconds(), spectacle.location, spectacle.price, request.user.get_full_name(), request.user.email)
2014-08-19 12:54:22 +02:00
send_mail ( " %s " % spectacle , mail ,
2013-09-05 22:20:52 +02:00
request . user . email , [ " bda-revente@lists.ens.fr " ] ,
2014-08-19 12:54:22 +02:00
fail_silently = False )
2013-09-05 22:20:52 +02:00
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 ( ) } )
2016-05-21 23:57:36 +02:00
@buro_required
def liste_spectacles_ics ( request ) :
spectacles = Spectacle . objects . order_by ( " date " ) . all ( )
for spectacle in spectacles :
spectacle . dtend = spectacle . date + timedelta ( seconds = 7200 )
return render ( request , " liste_spectacles.ics " ,
{ " spectacles " : spectacles } , content_type = " text/calendar " )