# 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 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 = Spectacle.objects.filter(tirage=tirage).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.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}) BdaFormSet = inlineformset_factory(Participant, ChoixSpectacle, fields=("spectacle","double_choice","priority"), formset=BaseBdaFormSet) 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 = Spectacle.objects.filter(tirage=tirage_elt).select_related().all() members = Participant.objects.filter(tirage=tirage_elt).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 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) return render(request, "bda-emails.html", {"spectacle": spectacle}) @buro_required def unpaid(request, tirage_id): tirage = get_object_or_404(Tirage, id=tirage_id) unpaid = Participants.objects.filter(tirage=tirage, paid=False).all() return render(request, "bda-unpaid.html", {"unpaid": unpaid}) @buro_required def liste_spectacles_ics(request, tirage_id): spectacles = Spectacle.objects.filter(tirage=tirage).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")