forked from DGNum/gestioCOF
Merge branch 'master' into Kerl/use_django_custommail
This commit is contained in:
commit
b9c2efaf0e
|
@ -225,7 +225,7 @@ class SpectacleReventeAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
list_display = ("spectacle", "seller", "date", "soldTo")
|
list_display = ("spectacle", "seller", "date", "soldTo")
|
||||||
raw_id_fields = ("attribution",)
|
raw_id_fields = ("attribution",)
|
||||||
readonly_fields = ("shotgun", "expiration_time")
|
readonly_fields = ("date_tirage",)
|
||||||
search_fields = ['attribution__spectacle__title',
|
search_fields = ['attribution__spectacle__title',
|
||||||
'seller__user__username',
|
'seller__user__username',
|
||||||
'seller__user__first_name',
|
'seller__user__first_name',
|
||||||
|
|
|
@ -68,9 +68,8 @@ class AnnulForm(forms.Form):
|
||||||
self.fields['attributions'].queryset = participant.attribution_set\
|
self.fields['attributions'].queryset = participant.attribution_set\
|
||||||
.filter(spectacle__date__gte=timezone.now(),
|
.filter(spectacle__date__gte=timezone.now(),
|
||||||
revente__isnull=False,
|
revente__isnull=False,
|
||||||
revente__date__gt=timezone.now()-timedelta(hours=1))\
|
revente__date__gt=timezone.now()-timedelta(hours=1),
|
||||||
.filter(Q(revente__soldTo__isnull=True) |
|
revente__soldTo__isnull=True)
|
||||||
Q(revente__soldTo=participant))
|
|
||||||
|
|
||||||
|
|
||||||
class InscriptionReventeForm(forms.Form):
|
class InscriptionReventeForm(forms.Form):
|
||||||
|
|
|
@ -36,7 +36,7 @@ class Command(BaseCommand):
|
||||||
revente.send_notif()
|
revente.send_notif()
|
||||||
self.stdout.write("Mail d'inscription à une revente envoyé")
|
self.stdout.write("Mail d'inscription à une revente envoyé")
|
||||||
# Check si tirage à faire
|
# Check si tirage à faire
|
||||||
elif (now >= revente.expiration_time and
|
elif (now >= revente.date_tirage and
|
||||||
not revente.tirage_done):
|
not revente.tirage_done):
|
||||||
self.stdout.write(str(now))
|
self.stdout.write(str(now))
|
||||||
revente.tirage()
|
revente.tirage()
|
||||||
|
|
33
bda/migrations/0010_spectaclerevente_shotgun.py
Normal file
33
bda/migrations/0010_spectaclerevente_shotgun.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models, migrations
|
||||||
|
from django.utils import timezone
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
|
||||||
|
def forwards_func(apps, schema_editor):
|
||||||
|
SpectacleRevente = apps.get_model("bda", "SpectacleRevente")
|
||||||
|
|
||||||
|
for revente in SpectacleRevente.objects.all():
|
||||||
|
is_expired = timezone.now() > revente.date_tirage()
|
||||||
|
is_direct = (revente.attribution.spectacle.date >= revente.date and
|
||||||
|
timezone.now() > revente.date + timedelta(minutes=15))
|
||||||
|
revente.shotgun = is_expired or is_direct
|
||||||
|
revente.save()
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('bda', '0009_revente'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='spectaclerevente',
|
||||||
|
name='shotgun',
|
||||||
|
field=models.BooleanField(default=False, verbose_name='Disponible imm\xe9diatement'),
|
||||||
|
),
|
||||||
|
migrations.RunPython(forwards_func, migrations.RunPython.noop),
|
||||||
|
]
|
|
@ -225,32 +225,20 @@ class SpectacleRevente(models.Model):
|
||||||
default=False)
|
default=False)
|
||||||
tirage_done = models.BooleanField("Tirage effectué",
|
tirage_done = models.BooleanField("Tirage effectué",
|
||||||
default=False)
|
default=False)
|
||||||
|
shotgun = models.BooleanField("Disponible immédiatement",
|
||||||
|
default=False)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def expiration_time(self):
|
def date_tirage(self):
|
||||||
|
"""Renvoie la date du tirage au sort de la revente."""
|
||||||
# L'acheteur doit être connu au plus 12h avant le spectacle
|
# L'acheteur doit être connu au plus 12h avant le spectacle
|
||||||
remaining_time = (self.attribution.spectacle.date
|
remaining_time = (self.attribution.spectacle.date
|
||||||
- self.date - timedelta(hours=13))
|
- self.date - timedelta(hours=13))
|
||||||
# Au minimum, on attend 2 jours avant le tirage
|
# Au minimum, on attend 2 jours avant le tirage
|
||||||
delay = min(remaining_time, timedelta(days=2))
|
delay = min(remaining_time, timedelta(days=2))
|
||||||
# On a aussi 1h pour changer d'avis
|
# Le vendeur a aussi 1h pour changer d'avis
|
||||||
return self.date + delay + timedelta(hours=1)
|
return self.date + delay + timedelta(hours=1)
|
||||||
|
|
||||||
def expiration_time_str(self):
|
|
||||||
return self.expiration_time \
|
|
||||||
.astimezone(timezone.get_current_timezone()) \
|
|
||||||
.strftime('%d/%m/%y à %H:%M')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def shotgun(self):
|
|
||||||
# Soit on a dépassé le délai du tirage, soit il reste peu de
|
|
||||||
# temps avant le spectacle
|
|
||||||
# On se laisse 5min de marge pour cron
|
|
||||||
return (timezone.now() > self.expiration_time + timedelta(minutes=5) or
|
|
||||||
(self.attribution.spectacle.date <= timezone.now() +
|
|
||||||
timedelta(days=1))) and (timezone.now() >= self.date +
|
|
||||||
timedelta(minutes=15))
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s -- %s" % (self.seller,
|
return "%s -- %s" % (self.seller,
|
||||||
self.attribution.spectacle.title)
|
self.attribution.spectacle.title)
|
||||||
|
@ -307,6 +295,9 @@ class SpectacleRevente(models.Model):
|
||||||
from_email=settings.MAIL_DATA['revente']['FROM']
|
from_email=settings.MAIL_DATA['revente']['FROM']
|
||||||
)
|
)
|
||||||
self.notif_sent = True
|
self.notif_sent = True
|
||||||
|
# Flag inutile, sauf si l'horloge interne merde
|
||||||
|
self.tirage_done = True
|
||||||
|
self.shotgun = True
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
def tirage(self):
|
def tirage(self):
|
||||||
|
@ -358,5 +349,8 @@ class SpectacleRevente(models.Model):
|
||||||
mails_data,
|
mails_data,
|
||||||
from_email=settings.MAIL_DATA['revente']['FROM']
|
from_email=settings.MAIL_DATA['revente']['FROM']
|
||||||
)
|
)
|
||||||
|
# Si personne ne veut de la place, elle part au shotgun
|
||||||
|
else:
|
||||||
|
self.shotgun = True
|
||||||
self.tirage_done = True
|
self.tirage_done = True
|
||||||
self.save()
|
self.save()
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
{% block realcontent %}
|
{% block realcontent %}
|
||||||
<h2>Inscription à une revente</h2>
|
<h2>Inscription à une revente</h2>
|
||||||
<p class="success"> Votre inscription pour a bien été enregistrée !</p>
|
<p class="success"> Votre inscription a bien été enregistrée !</p>
|
||||||
<p>Le tirage au sort pour cette revente ({{spectacle}}) sera effectué le {{date}}.
|
<p>Le tirage au sort pour cette revente ({{spectacle}}) sera effectué le {{date}}.
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
{% extends "base_title.html" %}
|
{% extends "base_title.html" %}
|
||||||
|
|
||||||
{% block realcontent %}
|
{% block realcontent %}
|
||||||
<h2>Nope</h2>
|
<h2>Nope</h2>
|
||||||
<p>Cette revente n'est pas disponible actuellement, désolé !</p>
|
{% if revente.shotgun %}
|
||||||
|
<p>Le tirage au sort de cette revente a déjà été effectué !</p>
|
||||||
|
|
||||||
|
<p>Si personne n'était intéressé, elle est maintenant disponible
|
||||||
|
<a href="{% url "bda-buy-revente" revente.attribution.spectacle.id %}">ici</a>.</p>
|
||||||
|
{% else %}
|
||||||
|
<p> Il n'est pas encore possible de s'inscrire à cette revente, réessaie dans quelque temps !</p>
|
||||||
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -8,7 +8,10 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if deja_revente %}
|
{% if deja_revente %}
|
||||||
<p class="success">Des reventes existent déjà pour certains de ces spectacles ; vérifie les places disponibles sans tirage !</p>
|
<p class="success">Des reventes existent déjà pour certains de ces spectacles ; vérifie les places disponibles sans tirage !</p>
|
||||||
|
{% elif inscrit_revente %}
|
||||||
|
<p class="success">Tu as été inscrit à une revente en cours pour ce spectacle !</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<form action="" class="form-horizontal" method="post">
|
<form action="" class="form-horizontal" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
90
bda/views.py
90
bda/views.py
|
@ -271,6 +271,7 @@ def revente(request, tirage_id):
|
||||||
if not participant.paid:
|
if not participant.paid:
|
||||||
return render(request, "bda-notpaid.html", {})
|
return render(request, "bda-notpaid.html", {})
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
|
# On met en vente une place
|
||||||
if 'resell' in request.POST:
|
if 'resell' in request.POST:
|
||||||
resellform = ResellForm(participant, request.POST, prefix='resell')
|
resellform = ResellForm(participant, request.POST, prefix='resell')
|
||||||
annulform = AnnulForm(participant, prefix='annul')
|
annulform = AnnulForm(participant, prefix='annul')
|
||||||
|
@ -279,12 +280,17 @@ def revente(request, tirage_id):
|
||||||
attributions = resellform.cleaned_data["attributions"]
|
attributions = resellform.cleaned_data["attributions"]
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
for attribution in attributions:
|
for attribution in attributions:
|
||||||
revente, created = SpectacleRevente.objects.get_or_create(
|
revente, created = \
|
||||||
|
SpectacleRevente.objects.get_or_create(
|
||||||
attribution=attribution,
|
attribution=attribution,
|
||||||
defaults={'seller': participant})
|
defaults={'seller': participant})
|
||||||
if not created:
|
if not created:
|
||||||
revente.seller = participant
|
revente.seller = participant
|
||||||
revente.date = timezone.now()
|
revente.date = timezone.now()
|
||||||
|
revente.soldTo = None
|
||||||
|
revente.notif_sent = False
|
||||||
|
revente.tirage_done = False
|
||||||
|
revente.shotgun = False
|
||||||
context = {
|
context = {
|
||||||
'vendeur': participant.user,
|
'vendeur': participant.user,
|
||||||
'show': attribution.spectacle,
|
'show': attribution.spectacle,
|
||||||
|
@ -297,7 +303,7 @@ def revente(request, tirage_id):
|
||||||
mails_data,
|
mails_data,
|
||||||
from_email=settings.MAIL_DATA['revente']['FROM']
|
from_email=settings.MAIL_DATA['revente']['FROM']
|
||||||
)
|
)
|
||||||
|
# On annule une revente
|
||||||
elif 'annul' in request.POST:
|
elif 'annul' in request.POST:
|
||||||
annulform = AnnulForm(participant, request.POST, prefix='annul')
|
annulform = AnnulForm(participant, request.POST, prefix='annul')
|
||||||
resellform = ResellForm(participant, prefix='resell')
|
resellform = ResellForm(participant, prefix='resell')
|
||||||
|
@ -305,7 +311,8 @@ def revente(request, tirage_id):
|
||||||
attributions = annulform.cleaned_data["attributions"]
|
attributions = annulform.cleaned_data["attributions"]
|
||||||
for attribution in attributions:
|
for attribution in attributions:
|
||||||
attribution.revente.delete()
|
attribution.revente.delete()
|
||||||
|
# On confirme une vente en transférant la place à la personne qui a
|
||||||
|
# gagné le tirage
|
||||||
elif 'transfer' in request.POST:
|
elif 'transfer' in request.POST:
|
||||||
resellform = ResellForm(participant, prefix='resell')
|
resellform = ResellForm(participant, prefix='resell')
|
||||||
annulform = AnnulForm(participant, prefix='annul')
|
annulform = AnnulForm(participant, prefix='annul')
|
||||||
|
@ -318,7 +325,9 @@ def revente(request, tirage_id):
|
||||||
attrib = revente.attribution
|
attrib = revente.attribution
|
||||||
attrib.participant = revente.soldTo
|
attrib.participant = revente.soldTo
|
||||||
attrib.save()
|
attrib.save()
|
||||||
|
# On annule la revente après le tirage au sort (par exemple si
|
||||||
|
# la personne qui a gagné le tirage ne se manifeste pas). La place est
|
||||||
|
# alors remise en vente
|
||||||
elif 'reinit' in request.POST:
|
elif 'reinit' in request.POST:
|
||||||
resellform = ResellForm(participant, prefix='resell')
|
resellform = ResellForm(participant, prefix='resell')
|
||||||
annulform = AnnulForm(participant, prefix='annul')
|
annulform = AnnulForm(participant, prefix='annul')
|
||||||
|
@ -332,6 +341,7 @@ def revente(request, tirage_id):
|
||||||
revente.soldTo = None
|
revente.soldTo = None
|
||||||
revente.notif_sent = False
|
revente.notif_sent = False
|
||||||
revente.tirage_done = False
|
revente.tirage_done = False
|
||||||
|
revente.shotgun = False
|
||||||
if revente.answered_mail:
|
if revente.answered_mail:
|
||||||
revente.answered_mail.clear()
|
revente.answered_mail.clear()
|
||||||
revente.save()
|
revente.save()
|
||||||
|
@ -352,8 +362,7 @@ def revente(request, tirage_id):
|
||||||
sold = participant.attribution_set.filter(
|
sold = participant.attribution_set.filter(
|
||||||
spectacle__date__gte=timezone.now(),
|
spectacle__date__gte=timezone.now(),
|
||||||
revente__isnull=False,
|
revente__isnull=False,
|
||||||
revente__soldTo__isnull=False).exclude(
|
revente__soldTo__isnull=False)
|
||||||
revente__soldTo=participant)
|
|
||||||
|
|
||||||
return render(request, "bda-revente.html",
|
return render(request, "bda-revente.html",
|
||||||
{'tirage': tirage, 'overdue': overdue, "sold": sold,
|
{'tirage': tirage, 'overdue': overdue, "sold": sold,
|
||||||
|
@ -365,13 +374,14 @@ def revente_interested(request, revente_id):
|
||||||
revente = get_object_or_404(SpectacleRevente, id=revente_id)
|
revente = get_object_or_404(SpectacleRevente, id=revente_id)
|
||||||
participant, created = Participant.objects.get_or_create(
|
participant, created = Participant.objects.get_or_create(
|
||||||
user=request.user, tirage=revente.attribution.spectacle.tirage)
|
user=request.user, tirage=revente.attribution.spectacle.tirage)
|
||||||
if timezone.now() < revente.date + timedelta(hours=1) or revente.shotgun:
|
if (timezone.now() < revente.date + timedelta(hours=1)) or revente.shotgun:
|
||||||
return render(request, "bda-wrongtime.html", {})
|
return render(request, "bda-wrongtime.html",
|
||||||
|
{"revente": revente})
|
||||||
|
|
||||||
revente.answered_mail.add(participant)
|
revente.answered_mail.add(participant)
|
||||||
return render(request, "bda-interested.html",
|
return render(request, "bda-interested.html",
|
||||||
{"spectacle": revente.attribution.spectacle,
|
{"spectacle": revente.attribution.spectacle,
|
||||||
"date": revente.expiration_time})
|
"date": revente.date_tirage})
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -379,23 +389,9 @@ def list_revente(request, tirage_id):
|
||||||
tirage = get_object_or_404(Tirage, id=tirage_id)
|
tirage = get_object_or_404(Tirage, id=tirage_id)
|
||||||
participant, created = Participant.objects.get_or_create(
|
participant, created = Participant.objects.get_or_create(
|
||||||
user=request.user, tirage=tirage)
|
user=request.user, tirage=tirage)
|
||||||
spectacles = tirage.spectacle_set.filter(
|
|
||||||
date__gte=timezone.now())
|
|
||||||
shotgun = []
|
|
||||||
deja_revente = False
|
deja_revente = False
|
||||||
success = False
|
success = False
|
||||||
for spectacle in spectacles:
|
inscrit_revente = False
|
||||||
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':
|
if request.method == 'POST':
|
||||||
form = InscriptionReventeForm(tirage, request.POST)
|
form = InscriptionReventeForm(tirage, request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
|
@ -405,15 +401,24 @@ def list_revente(request, tirage_id):
|
||||||
for spectacle in choices:
|
for spectacle in choices:
|
||||||
qset = SpectacleRevente.objects.filter(
|
qset = SpectacleRevente.objects.filter(
|
||||||
attribution__spectacle=spectacle)
|
attribution__spectacle=spectacle)
|
||||||
if qset.exists():
|
if qset.filter(shotgun=True, soldTo__isnull=True).exists():
|
||||||
# On l'inscrit à l'un des tirages au sort
|
# Une place est disponible au shotgun, on suggère à
|
||||||
for revente in qset.all():
|
# l'utilisateur d'aller la récupérer
|
||||||
if revente.shotgun and not revente.soldTo:
|
|
||||||
deja_revente = True
|
deja_revente = True
|
||||||
else:
|
else:
|
||||||
revente.answered_mail.add(participant)
|
# La place n'est pas disponible au shotgun, si des reventes
|
||||||
revente.save()
|
# pour ce spectacle existent déjà, on inscrit la personne à
|
||||||
break
|
# la revente ayant le moins d'inscrits
|
||||||
|
min_resell = (
|
||||||
|
qset.filter(shotgun=False)
|
||||||
|
.annotate(nb_subscribers=Count('answered_mail'))
|
||||||
|
.order_by('nb_subscribers')
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
if min_resell is not None:
|
||||||
|
min_resell.answered_mail.add(participant)
|
||||||
|
min_resell.save()
|
||||||
|
inscrit_revente = True
|
||||||
success = True
|
success = True
|
||||||
else:
|
else:
|
||||||
form = InscriptionReventeForm(
|
form = InscriptionReventeForm(
|
||||||
|
@ -421,8 +426,9 @@ def list_revente(request, tirage_id):
|
||||||
initial={'spectacles': participant.choicesrevente.all()})
|
initial={'spectacles': participant.choicesrevente.all()})
|
||||||
|
|
||||||
return render(request, "liste-reventes.html",
|
return render(request, "liste-reventes.html",
|
||||||
{"form": form, 'shotgun': shotgun,
|
{"form": form,
|
||||||
"deja_revente": deja_revente, "success": success})
|
"deja_revente": deja_revente, "success": success,
|
||||||
|
"inscrit_revente": inscrit_revente})
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -434,15 +440,16 @@ def buy_revente(request, spectacle_id):
|
||||||
reventes = SpectacleRevente.objects.filter(
|
reventes = SpectacleRevente.objects.filter(
|
||||||
attribution__spectacle=spectacle,
|
attribution__spectacle=spectacle,
|
||||||
soldTo__isnull=True)
|
soldTo__isnull=True)
|
||||||
|
|
||||||
|
# Si l'utilisateur veut racheter une place qu'il est en train de revendre,
|
||||||
|
# on supprime la revente en question.
|
||||||
if reventes.filter(seller=participant).exists():
|
if reventes.filter(seller=participant).exists():
|
||||||
revente = reventes.filter(seller=participant)[0]
|
revente = reventes.filter(seller=participant)[0]
|
||||||
revente.delete()
|
revente.delete()
|
||||||
return HttpResponseRedirect(reverse("bda-shotgun",
|
return HttpResponseRedirect(reverse("bda-shotgun",
|
||||||
args=[tirage.id]))
|
args=[tirage.id]))
|
||||||
reventes_shotgun = []
|
|
||||||
for revente in reventes.all():
|
reventes_shotgun = list(reventes.filter(shotgun=True).all())
|
||||||
if revente.shotgun:
|
|
||||||
reventes_shotgun.append(revente)
|
|
||||||
|
|
||||||
if not reventes_shotgun:
|
if not reventes_shotgun:
|
||||||
return render(request, "bda-no-revente.html", {})
|
return render(request, "bda-no-revente.html", {})
|
||||||
|
@ -478,14 +485,11 @@ def revente_shotgun(request, tirage_id):
|
||||||
date__gte=timezone.now())
|
date__gte=timezone.now())
|
||||||
shotgun = []
|
shotgun = []
|
||||||
for spectacle in spectacles:
|
for spectacle in spectacles:
|
||||||
revente_objects = SpectacleRevente.objects.filter(
|
reventes = SpectacleRevente.objects.filter(
|
||||||
attribution__spectacle=spectacle,
|
attribution__spectacle=spectacle,
|
||||||
|
shotgun=True,
|
||||||
soldTo__isnull=True)
|
soldTo__isnull=True)
|
||||||
revente_count = 0
|
if reventes.exists():
|
||||||
for revente in revente_objects:
|
|
||||||
if revente.shotgun:
|
|
||||||
revente_count += 1
|
|
||||||
if revente_count:
|
|
||||||
shotgun.append(spectacle)
|
shotgun.append(spectacle)
|
||||||
|
|
||||||
return render(request, "bda-shotgun.html",
|
return render(request, "bda-shotgun.html",
|
||||||
|
|
Loading…
Reference in a new issue