kpsul/bda/views.py

378 lines
15 KiB
Python
Raw Normal View History

2012-07-11 17:39:20 +02:00
# coding: utf-8
from __future__ import division
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
from django.db import models
from django.core import serializers
from django.forms.models import inlineformset_factory
import hashlib
2012-07-11 17:39:20 +02:00
from django.core.mail import send_mail
from django.utils import timezone
from django.views.generic.list import ListView
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.contrib import messages
from datetime import timedelta
import time
from gestioncof.decorators import cof_required, buro_required
from bda.models import Spectacle, Participant, ChoixSpectacle, Attribution, Tirage
from bda.algorithm import Algorithm
2012-07-11 17:39:20 +02:00
from bda.forms import BaseBdaFormSet, TokenForm, ResellForm
2012-07-11 17:39:20 +02:00
2013-10-01 15:27:19 +02:00
@cof_required
def etat_places(request, tirage_id):
tirage = get_object_or_404(Tirage, id=tirage_id)
2016-06-06 11:11:33 +02:00
spectacles1 = ChoixSpectacle.objects \
.filter(spectacle__tirage=tirage) \
.filter(double_choice="1") \
.all() \
.values('spectacle','spectacle__title') \
.annotate(total=models.Count('spectacle'))
spectacles2 = ChoixSpectacle.objects \
.filter(spectacle__tirage=tirage) \
.exclude(double_choice="1") \
.all() \
.values('spectacle','spectacle__title') \
.annotate(total=models.Count('spectacle'))
spectacles = tirage.spectacle_set.all()
spectacles_dict = {}
total = 0
for spectacle in spectacles:
spectacle.total = 0
2015-01-06 11:01:15 +01:00
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 / \
spectacles_dict[spectacle["spectacle"]].slots
total += spectacle["total"]
for spectacle in spectacles2:
spectacles_dict[spectacle["spectacle"]].total += 2*spectacle["total"]
spectacles_dict[spectacle["spectacle"]].ratio = \
spectacles_dict[spectacle["spectacle"]].total / \
spectacles_dict[spectacle["spectacle"]].slots
total += spectacle["total"]
return render(request, "etat-places.html",
{"spectacles": spectacles, "total": total, 'tirage': tirage})
def _hash_queryset(queryset):
data = serializers.serialize("json", queryset)
hasher = hashlib.sha256()
hasher.update(data)
return hasher.hexdigest()
@cof_required
def places(request, tirage_id):
tirage = get_object_or_404(Tirage, id=tirage_id)
participant, created = Participant.objects.get_or_create(
user=request.user, tirage=tirage)
places = participant.attribution_set.order_by(
"spectacle__date", "spectacle").all()
total = sum([place.spectacle.price for place in places])
filtered_places = []
places_dict = {}
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,
"tirage": tirage,
"total": total,
"warning": warning})
@cof_required
def places_ics(request, tirage_id):
tirage = get_object_or_404(Tirage, id=tirage_id)
participant, created = Participant.objects.get_or_create(
user=request.user, tirage=tirage)
places = participant.attribution_set.order_by(
"spectacle__date", "spectacle").all()
filtered_places = []
places_dict = {}
spectacles = []
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)
return render(request, "resume_places.ics",
{"participant": participant,
"places": filtered_places}, content_type="text/calendar")
@cof_required
def inscription(request, tirage_id):
tirage = get_object_or_404(Tirage, id=tirage_id)
if timezone.now() < tirage.ouverture:
error_desc = "Ouverture le %s" % (
tirage.ouverture.strftime('%d %b %Y à %H:%M'))
return render(request, 'resume_inscription.html',
{ "error_title": "Le tirage n'est pas encore ouvert !",
"error_description": error_desc })
if timezone.now() > tirage.fermeture:
participant, created = Participant.objects.get_or_create(
user=request.user, tirage=tirage)
choices = participant.choixspectacle_set.order_by("priority").all()
return render(request, "resume_inscription.html",
{ "error_title": "C'est fini !",
"error_description": u"Tirage au sort dans la journée !",
"choices": choices})
def formfield_callback(f, **kwargs):
if f.name == "spectacle":
kwargs['queryset'] = tirage.spectacle_set
return f.formfield(**kwargs)
BdaFormSet = inlineformset_factory(
Participant,
2016-06-05 23:13:22 +02:00
ChoixSpectacle,
fields=("spectacle","double_choice","priority"),
formset=BaseBdaFormSet,
formfield_callback=formfield_callback)
participant, created = Participant.objects.get_or_create(
user=request.user, tirage=tirage)
2012-07-11 17:39:20 +02:00
success = False
stateerror = False
2012-07-11 17:39:20 +02:00
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():
formset.save()
success = True
formset = BdaFormSet(instance=participant)
2012-07-11 17:39:20 +02:00
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,
'tirage': tirage,
"stateerror": stateerror})
def do_tirage(request, tirage_id):
tirage_elt = get_object_or_404(Tirage, id=tirage_id)
form = TokenForm(request.POST)
if not form.is_valid():
return tirage(request)
tirage_elt.token = form.cleaned_data['token']
tirage_elt.save()
start = time.time()
data = {}
shows = tirage_elt.spectacle_set.select_related().all()
members = tirage_elt.participant_set.all()
choices = ChoixSpectacle.objects.filter(spectacle__tirage=tirage_elt).order_by(
'participant', 'priority').select_related().all()
algo = Algorithm(shows, members, choices)
results = algo(form.cleaned_data["token"])
total_slots = 0
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)
# À partir d'ici, le tirage devient effectif
2016-06-07 00:18:16 +02:00
# FIXME: Établir les conditions de validations (formulaire ?)
# cf. issue #32
2016-06-06 13:20:14 +02:00
if False:
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)
@buro_required
def tirage(request, tirage_id):
if request.POST:
form = TokenForm(request.POST)
if form.is_valid():
return do_tirage(request, tirage_id)
else:
form = TokenForm()
return render(request, "bda-token.html", {"form": form})
def do_resell(request, form):
spectacle = form.cleaned_data["spectacle"]
count = form.cleaned_data["count"]
places = "2 places" if count == "2" else "une place"
mail = u"""Bonjour,
Je souhaite revendre %s pour %s le %s (%s) à %.02f.
Contactez moi par email si vous êtes intéressé·e·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 = False)
return render(request, "bda-success.html", {"show": spectacle, "places": places})
@login_required
def revente(request, tirage_id):
tirage = get_object_or_404(Tirage, id=tirage_id)
participant, created = Participant.objects.get_or_create(
user=request.user, tirage=tirage)
if not participant.paid:
return render(request, "bda-notpaid.html", {})
if request.POST:
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, 'tirage': tirage})
@buro_required
def spectacle(request, tirage_id, spectacle_id):
tirage = get_object_or_404(Tirage, id=tirage_id)
spectacle = get_object_or_404(Spectacle, id=spectacle_id, tirage=tirage)
attributions = spectacle.attribues.all()
2016-06-10 18:04:02 +02:00
participants = {}
for attrib in attributions:
participant = attrib.participant
participant_info = {'lastname':participant.user.last_name,
'name': participant.user.get_full_name,
'username':participant.user.username,
'email':participant.user.email,
'given':attrib.given,
'paid':participant.paid,
'nb_places':1}
2016-06-12 19:29:50 +02:00
if participant.id in participants:
participants[participant.id]['nb_places'] = 2
else:
participants[participant.id] = participant_info
participants_info = sorted(participants.values(),
key=lambda part: part['lastname'])
return render(request, "bda-participants.html",
{"spectacle": spectacle, "participants": participants_info})
@buro_required
def add_attrib(request, tirage_id, spectacle_id):
tirage = get_object_or_404(Tirage, id=tirage_id)
spectacle = get_object_or_404(Spectacle, id = spectacle_id, tirage=tirage)
try:
part=tirage.participant_set.get(user__username=request.POST['username'])
except Participant.DoesNotExist:
messages.add_message(request, messages.ERROR,
u"Erreur : utilisateur %s non trouvé" % request.POST['username'])
return HttpResponseRedirect(reverse('bda-spectacle',
args=(tirage_id, spectacle_id,)))
places_owned = len(spectacle.attribues.filter(participant=part))
if int(request.POST['nb_places'])+places_owned > 2:
messages.add_message(request, messages.ERROR,
"Erreur: on ne peut pas avoir plus de deux places")
return HttpResponseRedirect(reverse('bda-spectacle',
args=(tirage_id, spectacle_id,)))
attrib = Attribution(participant=part, spectacle=spectacle,
given=('given' in request.POST))
attrib.save()
if request.POST['nb_places']==2:
attrib.save()
messages.add_message(request, messages.SUCCESS,
"Attribution réussie !")
return HttpResponseRedirect(reverse('bda-spectacle',
args=(tirage_id, spectacle_id,)))
class SpectacleListView(ListView):
model = Spectacle
template_name = 'spectacle_list.html'
def get_queryset(self):
self.tirage = get_object_or_404(Tirage, id=self.kwargs['tirage_id'])
categories = self.tirage.spectacle_set.all()
return categories
def get_context_data(self, **kwargs):
context = super(SpectacleListView, self).get_context_data(**kwargs)
context['tirage_id'] = self.tirage.id
2016-06-10 18:04:02 +02:00
context['tirage_name'] = self.tirage.title
return context
@buro_required
def unpaid(request, tirage_id):
tirage = get_object_or_404(Tirage, id=tirage_id)
unpaid = tirage.participant_set.filter(paid=False).all()
return render(request, "bda-unpaid.html", {"unpaid": unpaid})
@buro_required
def liste_spectacles_ics(request, tirage_id):
tirage = get_object_or_404(Tirage, id=tirage_id)
spectacles = tirage.spectacle_set.order_by("date").all()
for spectacle in spectacles:
spectacle.dtend = spectacle.date + timedelta(seconds=7200)
return render(request, "liste_spectacles.ics",
{"spectacles": spectacles, "tirage": tirage},
content_type="text/calendar")