d6dd7b346c
Améliore les mails automatiques du BdA Les mails du BdA sont maintenant tous chargés depuis des templates gérés par le système de templates de Django, et plus par de l'interpolation de chaîne de caractères. Ceci permet en particulier d'utiliser (et de configurer) la localisation de Django afin d'afficher les dates de façon uniforme (et sans "hack" à la `date_no_seconds`) dans un format comportant un "à" entre le jour et l'heure. See merge request !113
588 lines
23 KiB
Python
588 lines
23 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
from __future__ import division
|
|
from __future__ import print_function
|
|
from __future__ import unicode_literals
|
|
|
|
import random
|
|
|
|
from django.shortcuts import render, get_object_or_404
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.db import models, transaction
|
|
from django.db.models import Count, Q
|
|
from django.core import serializers, mail
|
|
from django.forms.models import inlineformset_factory
|
|
from django.http import HttpResponseBadRequest, HttpResponseRedirect
|
|
from django.core.urlresolvers import reverse
|
|
from django.conf import settings
|
|
import hashlib
|
|
|
|
from django.core.mail import send_mail
|
|
from django.template import loader
|
|
from django.utils import timezone
|
|
from django.views.generic.list import ListView
|
|
|
|
import time
|
|
from datetime import timedelta
|
|
|
|
from gestioncof.decorators import cof_required, buro_required
|
|
from bda.models import Spectacle, Participant, ChoixSpectacle, Attribution,\
|
|
Tirage, SpectacleRevente
|
|
from bda.algorithm import Algorithm
|
|
|
|
from bda.forms import BaseBdaFormSet, TokenForm, ResellForm, AnnulForm,\
|
|
InscriptionReventeForm
|
|
|
|
|
|
@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 / \
|
|
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).encode('utf-8')
|
|
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 inscription(request, tirage_id):
|
|
tirage = get_object_or_404(Tirage, id=tirage_id)
|
|
if timezone.now() < tirage.ouverture:
|
|
error_desc = tirage.ouverture.strftime('Ouverture le %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":
|
|
"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_id)
|
|
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 "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 = {}
|
|
# Participant objects are not shared accross spectacle results,
|
|
# so assign a single object for each Participant id
|
|
members_uniq = {}
|
|
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.filter(spectacle__tirage=tirage_elt).delete()
|
|
tirage_elt.tokens += "%s\n\"\"\"%s\"\"\"\n" % (
|
|
timezone.now().strftime("%y-%m-%d %H:%M:%S"),
|
|
form.cleaned_data['token'])
|
|
tirage_elt.enable_do_tirage = False
|
|
tirage_elt.save()
|
|
Attribution.objects.bulk_create([
|
|
Attribution(spectacle=show, participant=member)
|
|
for show, members, _ in results
|
|
for member, _, _, _ in members])
|
|
# On inscrit à BdA-Revente ceux qui n'ont pas eu les places voulues
|
|
for (show, _, losers) in results:
|
|
for (loser, _, _, _) in losers:
|
|
loser.choicesrevente.add(show)
|
|
loser.save()
|
|
return render(request, "bda-attrib-extra.html", data)
|
|
else:
|
|
return render(request, "bda-attrib.html", data)
|
|
|
|
|
|
@buro_required
|
|
def tirage(request, tirage_id):
|
|
tirage_elt = get_object_or_404(Tirage, id=tirage_id)
|
|
if not (tirage_elt.enable_do_tirage
|
|
and tirage_elt.fermeture < timezone.now()):
|
|
return render(request, "tirage-failed.html", {'tirage': tirage_elt})
|
|
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})
|
|
|
|
|
|
@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.method == 'POST':
|
|
if 'resell' in request.POST:
|
|
resellform = ResellForm(participant, request.POST, prefix='resell')
|
|
annulform = AnnulForm(participant, prefix='annul')
|
|
if resellform.is_valid():
|
|
mails = []
|
|
attributions = resellform.cleaned_data["attributions"]
|
|
with transaction.atomic():
|
|
for attribution in attributions:
|
|
revente, created = SpectacleRevente.objects.get_or_create(
|
|
attribution=attribution,
|
|
defaults={'seller': participant})
|
|
if not created:
|
|
revente.seller = participant
|
|
revente.date = timezone.now()
|
|
mail_subject = "BdA-Revente : {:s}".format(attribution.spectacle.title)
|
|
mail_body = loader.render_to_string('bda/mails/revente-new.txt', {
|
|
'vendeur': participant.user,
|
|
'spectacle': attribution.spectacle,
|
|
'revente': revente,
|
|
})
|
|
mails.append(mail.EmailMessage(
|
|
mail_subject, mail_body,
|
|
from_email=settings.MAIL_DATA['revente']['FROM'],
|
|
to=[participant.user.email],
|
|
reply_to=[settings.MAIL_DATA['revente']['REPLYTO']],
|
|
))
|
|
revente.save()
|
|
mail.get_connection().send_messages(mails)
|
|
|
|
elif 'annul' in request.POST:
|
|
annulform = AnnulForm(participant, request.POST, prefix='annul')
|
|
resellform = ResellForm(participant, prefix='resell')
|
|
if annulform.is_valid():
|
|
attributions = annulform.cleaned_data["attributions"]
|
|
for attribution in attributions:
|
|
attribution.revente.delete()
|
|
|
|
elif 'transfer' in request.POST:
|
|
resellform = ResellForm(participant, prefix='resell')
|
|
annulform = AnnulForm(participant, prefix='annul')
|
|
|
|
revente_id = request.POST['transfer'][0]
|
|
rev = SpectacleRevente.objects.filter(soldTo__isnull=False,
|
|
id=revente_id)
|
|
if rev.exists():
|
|
revente = rev.get()
|
|
attrib = revente.attribution
|
|
attrib.participant = revente.soldTo
|
|
attrib.save()
|
|
|
|
elif 'reinit' in request.POST:
|
|
resellform = ResellForm(participant, prefix='resell')
|
|
annulform = AnnulForm(participant, prefix='annul')
|
|
revente_id = request.POST['reinit'][0]
|
|
rev = SpectacleRevente.objects.filter(soldTo__isnull=False,
|
|
id=revente_id)
|
|
if rev.exists():
|
|
revente = rev.get()
|
|
if revente.attribution.spectacle.date > timezone.now():
|
|
revente.date = timezone.now() - timedelta(hours=1)
|
|
revente.soldTo = None
|
|
revente.notif_sent = False
|
|
revente.tirage_done = False
|
|
if revente.answered_mail:
|
|
revente.answered_mail.clear()
|
|
revente.save()
|
|
|
|
else:
|
|
resellform = ResellForm(participant, prefix='resell')
|
|
annulform = AnnulForm(participant, prefix='annul')
|
|
else:
|
|
resellform = ResellForm(participant, prefix='resell')
|
|
annulform = AnnulForm(participant, prefix='annul')
|
|
|
|
overdue = participant.attribution_set.filter(
|
|
spectacle__date__gte=timezone.now(),
|
|
revente__isnull=False,
|
|
revente__seller=participant,
|
|
revente__date__lte=timezone.now()-timedelta(hours=1)).filter(
|
|
Q(revente__soldTo__isnull=True) | Q(revente__soldTo=participant))
|
|
sold = participant.attribution_set.filter(
|
|
spectacle__date__gte=timezone.now(),
|
|
revente__isnull=False,
|
|
revente__soldTo__isnull=False).exclude(
|
|
revente__soldTo=participant)
|
|
|
|
return render(request, "bda-revente.html",
|
|
{'tirage': tirage, 'overdue': overdue, "sold": sold,
|
|
"annulform": annulform, "resellform": resellform})
|
|
|
|
|
|
@login_required
|
|
def revente_interested(request, revente_id):
|
|
revente = get_object_or_404(SpectacleRevente, id=revente_id)
|
|
participant, created = Participant.objects.get_or_create(
|
|
user=request.user, tirage=revente.attribution.spectacle.tirage)
|
|
if timezone.now() < revente.date + timedelta(hours=1) or revente.shotgun:
|
|
return render(request, "bda-wrongtime.html", {})
|
|
|
|
revente.answered_mail.add(participant)
|
|
return render(request, "bda-interested.html",
|
|
{"spectacle": revente.attribution.spectacle,
|
|
"date": revente.expiration_time})
|
|
|
|
|
|
@login_required
|
|
def list_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)
|
|
spectacles = tirage.spectacle_set.filter(
|
|
date__gte=timezone.now())
|
|
shotgun = []
|
|
deja_revente = False
|
|
success = False
|
|
for spectacle in spectacles:
|
|
revente_objects = SpectacleRevente.objects.filter(
|
|
attribution__spectacle=spectacle,
|
|
soldTo__isnull=True)
|
|
revente_count = 0
|
|
for revente in revente_objects:
|
|
if revente.shotgun:
|
|
revente_count += 1
|
|
if revente_count:
|
|
spectacle.revente_count = revente_count
|
|
shotgun.append(spectacle)
|
|
|
|
if request.method == 'POST':
|
|
form = InscriptionReventeForm(tirage, request.POST)
|
|
if form.is_valid():
|
|
choices = form.cleaned_data['spectacles']
|
|
participant.choicesrevente = choices
|
|
participant.save()
|
|
for spectacle in choices:
|
|
qset = SpectacleRevente.objects.filter(
|
|
attribution__spectacle=spectacle)
|
|
if qset.exists():
|
|
# On l'inscrit à l'un des tirages au sort
|
|
for revente in qset.all():
|
|
if revente.shotgun and not revente.soldTo:
|
|
deja_revente = True
|
|
else:
|
|
revente.answered_mail.add(participant)
|
|
revente.save()
|
|
break
|
|
success = True
|
|
else:
|
|
form = InscriptionReventeForm(
|
|
tirage,
|
|
initial={'spectacles': participant.choicesrevente.all()})
|
|
|
|
return render(request, "liste-reventes.html",
|
|
{"form": form, 'shotgun': shotgun,
|
|
"deja_revente": deja_revente, "success": success})
|
|
|
|
|
|
@login_required
|
|
def buy_revente(request, spectacle_id):
|
|
spectacle = get_object_or_404(Spectacle, id=spectacle_id)
|
|
tirage = spectacle.tirage
|
|
participant, created = Participant.objects.get_or_create(
|
|
user=request.user, tirage=tirage)
|
|
reventes = SpectacleRevente.objects.filter(
|
|
attribution__spectacle=spectacle,
|
|
soldTo__isnull=True)
|
|
if reventes.filter(seller=participant).exists():
|
|
revente = reventes.filter(seller=participant)[0]
|
|
revente.delete()
|
|
return HttpResponseRedirect(reverse("bda-shotgun",
|
|
args=[tirage.id]))
|
|
reventes_shotgun = []
|
|
for revente in reventes.all():
|
|
if revente.shotgun:
|
|
reventes_shotgun.append(revente)
|
|
|
|
if not reventes_shotgun:
|
|
return render(request, "bda-no-revente.html", {})
|
|
|
|
if request.POST:
|
|
revente = random.choice(reventes_shotgun)
|
|
revente.soldTo = participant
|
|
revente.save()
|
|
mail = loader.render_to_string('bda/mails/buy-shotgun.txt', {
|
|
'spectacle': spectacle,
|
|
'acheteur': request.user,
|
|
'vendeur': revente.seller.user,
|
|
})
|
|
send_mail("BdA-Revente : %s" % spectacle.title, mail,
|
|
request.user.email,
|
|
[revente.seller.user.email],
|
|
fail_silently=False)
|
|
return render(request, "bda-success.html",
|
|
{"seller": revente.attribution.participant.user,
|
|
"spectacle": spectacle})
|
|
|
|
return render(request, "revente-confirm.html",
|
|
{"spectacle": spectacle,
|
|
"user": request.user})
|
|
|
|
|
|
@login_required
|
|
def revente_shotgun(request, tirage_id):
|
|
tirage = get_object_or_404(Tirage, id=tirage_id)
|
|
spectacles = tirage.spectacle_set.filter(
|
|
date__gte=timezone.now())
|
|
shotgun = []
|
|
for spectacle in spectacles:
|
|
revente_objects = SpectacleRevente.objects.filter(
|
|
attribution__spectacle=spectacle,
|
|
soldTo__isnull=True)
|
|
revente_count = 0
|
|
for revente in revente_objects:
|
|
if revente.shotgun:
|
|
revente_count += 1
|
|
if revente_count:
|
|
shotgun.append(spectacle)
|
|
|
|
return render(request, "bda-shotgun.html",
|
|
{"shotgun": shotgun})
|
|
|
|
|
|
@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
|
|
participant_info = {'lastname': participant.user.last_name,
|
|
'name': participant.user.get_full_name,
|
|
'username': participant.user.username,
|
|
'email': participant.user.email,
|
|
'given': int(attrib.given),
|
|
'paid': participant.paid,
|
|
'nb_places': 1}
|
|
if participant.id in participants:
|
|
participants[participant.id]['nb_places'] += 1
|
|
participants[participant.id]['given'] += attrib.given
|
|
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})
|
|
|
|
|
|
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
|
|
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 \
|
|
.annotate(nb_attributions=Count('attribution')) \
|
|
.filter(paid=False, nb_attributions__gt=0).all()
|
|
return render(request, "bda-unpaid.html", {"unpaid": unpaid})
|
|
|
|
|
|
@buro_required
|
|
def send_rappel(request, spectacle_id):
|
|
show = get_object_or_404(Spectacle, id=spectacle_id)
|
|
# Mails d'exemples
|
|
fake_member = request.user
|
|
fake_member.nb_attr = 1
|
|
exemple_mail_1place = loader.render_to_string('bda/mails/rappel.txt', {
|
|
'member': fake_member,
|
|
'show': show})
|
|
fake_member.nb_attr = 2
|
|
exemple_mail_2places = loader.render_to_string('bda/mails/rappel.txt', {
|
|
'member': fake_member,
|
|
'show': show})
|
|
# Contexte
|
|
ctxt = {'show': show,
|
|
'exemple_mail_1place': exemple_mail_1place,
|
|
'exemple_mail_2places': exemple_mail_2places}
|
|
# Envoi confirmé
|
|
if request.method == 'POST':
|
|
members = show.send_rappel()
|
|
ctxt['sent'] = True
|
|
ctxt['members'] = members
|
|
# Demande de confirmation
|
|
else:
|
|
ctxt['sent'] = False
|
|
return render(request, "mails-rappel.html", ctxt)
|
|
|
|
|
|
def descriptions_spectacles(request, tirage_id):
|
|
tirage = get_object_or_404(Tirage, id=tirage_id)
|
|
shows_qs = tirage.spectacle_set
|
|
category_name = request.GET.get('category', '')
|
|
location_id = request.GET.get('location', '')
|
|
if category_name:
|
|
shows_qs = shows_qs.filter(category__name=category_name)
|
|
if location_id:
|
|
try:
|
|
shows_qs = shows_qs.filter(location__id=int(location_id))
|
|
except ValueError:
|
|
return HttpResponseBadRequest(
|
|
"La variable GET 'location' doit contenir un entier")
|
|
return render(request, 'descriptions.html', {'shows': shows_qs.all()})
|