gestioCOF/bda/views.py

349 lines
14 KiB
Python

# coding: utf-8
from django.contrib.auth.models import User
from django.shortcuts import render, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.db import models
from django.http import Http404
from django.core import serializers
from django.forms.models import inlineformset_factory
import hashlib
from django.core.mail import send_mail
from django.utils import timezone
from django.views.generic.list import ListView
from datetime import timedelta
import time
from gestioncof.decorators import cof_required, buro_required
from gestioncof.shared import send_custom_mail
from bda.models import Spectacle, Participant, ChoixSpectacle, Attribution, Tirage
from bda.algorithm import Algorithm
from bda.forms import BaseBdaFormSet, TokenForm, ResellForm
@cof_required
def etat_places(request, tirage_id):
tirage = get_object_or_404(Tirage, id=tirage_id)
spectacles1 = ChoixSpectacle.objects \
.filter(spectacle__tirage=tirage) \
.filter(double_choice="1") \
.all() \
.values('spectacle','spectacle__title') \
.annotate(total=models.Count('spectacle'))
spectacles2 = ChoixSpectacle.objects \
.filter(spectacle__tirage=tirage) \
.exclude(double_choice="1") \
.all() \
.values('spectacle','spectacle__title') \
.annotate(total=models.Count('spectacle'))
spectacles = tirage.spectacle_set.all()
spectacles_dict = {}
total = 0
for spectacle in spectacles:
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 += 2*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, '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)
date = place.spectacle.date.date()
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,
ChoixSpectacle,
fields=("spectacle","double_choice","priority"),
formset=BaseBdaFormSet,
formfield_callback=formfield_callback)
participant, created = Participant.objects.get_or_create(
user=request.user, tirage=tirage)
success = False
stateerror = False
if request.method == "POST":
dbstate = _hash_queryset(participant.choixspectacle_set.all())
if "dbstate" in request.POST and dbstate != request.POST["dbstate"]:
stateerror = True
formset = BdaFormSet(instance=participant)
else:
formset = BdaFormSet(request.POST, instance=participant)
if formset.is_valid():
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,
'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
# FIXME: Établir les conditions de validations (formulaire ?)
# cf. issue #32
if False:
Attribution.objects.all().delete()
for (show, members, _) in results:
for (member, _, _, _) in members:
attrib = Attribution(spectacle=show, participant=member)
attrib.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"
"""
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)
"""
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()
participants = []
for attrib in attributions:
participant = attrib.participant
if (participant in participants):
participant.nb_places = 2
else:
participant.nb_places = 1
participants.append(participant)
return render(request, "bda-participants.html", {"spectacle": spectacle, "participants": participants})
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
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")