From f44b3fc33c4df8e28ff4d7341e726cda152bb39d Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Fri, 22 Jul 2016 22:48:09 +0200 Subject: [PATCH 01/42] creates initial models --- bda/admin.py | 21 ++++++++++++++++++++- bda/models.py | 23 +++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/bda/admin.py b/bda/admin.py index 8f5915d5..3717e438 100644 --- a/bda/admin.py +++ b/bda/admin.py @@ -9,7 +9,7 @@ from django.core.mail import send_mail from django.contrib import admin from django.db.models import Sum, Count from bda.models import Spectacle, Salle, Participant, ChoixSpectacle,\ - Attribution, Tirage + Attribution, Tirage, SpectacleRevente from django import forms from datetime import timedelta @@ -204,9 +204,28 @@ class SalleAdmin(admin.ModelAdmin): search_fields = ('name', 'address') +class SpectacleReventeAdmin(admin.ModelAdmin): + model = SpectacleRevente + + def spectacle(self, obj): + return obj.attribution.spectacle + + def participant(self, obj): + return obj.attribution.participant + + list_display = ("spectacle", "participant", "date", "sold") + readonly_fields = ("shotgun",) + list_filter = ("sold", ) + search_fields = ("spectacle__title", + "participant__user__username", + "participant__user__firstname", + "participant__user__lastname",) + + admin.site.register(Spectacle, SpectacleAdmin) admin.site.register(Salle, SalleAdmin) admin.site.register(Participant, ParticipantAdmin) admin.site.register(Attribution, AttributionAdmin) admin.site.register(ChoixSpectacle, ChoixSpectacleAdmin) admin.site.register(Tirage, TirageAdmin) +admin.site.register(SpectacleRevente, SpectacleReventeAdmin) diff --git a/bda/models.py b/bda/models.py index 41037643..405236b9 100644 --- a/bda/models.py +++ b/bda/models.py @@ -5,6 +5,7 @@ from __future__ import print_function from __future__ import unicode_literals import calendar +import datetime from django.db import models from django.contrib.auth.models import User @@ -179,3 +180,25 @@ class Attribution(models.Model): def __str__(self): return "%s -- %s" % (self.participant, self.spectacle) + + +@python_2_unicode_compatible +class SpectacleRevente(models.Model): + attribution = models.OneToOneField(Attribution) + date = models.DateTimeField("Date de mise en vente", + default=timezone.now) + sold = models.BooleanField("Vendue", default=False) + + def get_expiration_time(self): + remaining_time = (self.attribution.spectacle.date - self.date) + delay = max(datetime.timedelta(hours=2), + min(remaining_time/2, datetime.timedelta(days=2))) + return self.date + delay + expiration_time = property(get_expiration_time) + + def get_shotgun(self): + return timezone.now > self.expiration_time + shotgun = property(get_shotgun) + + def __str__(self): + return "%s" % self.attribution From 6793194adccb275f5a79d8a1d61bd1812f58d6ad Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sat, 23 Jul 2016 22:21:30 +0200 Subject: [PATCH 02/42] fix affichage admin --- bda/admin.py | 3 ++- bda/models.py | 10 ++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/bda/admin.py b/bda/admin.py index 3717e438..05947534 100644 --- a/bda/admin.py +++ b/bda/admin.py @@ -214,7 +214,8 @@ class SpectacleReventeAdmin(admin.ModelAdmin): return obj.attribution.participant list_display = ("spectacle", "participant", "date", "sold") - readonly_fields = ("shotgun",) + raw_id_fields = ("attribution",) + readonly_fields = ("shotgun", "expiration_time") list_filter = ("sold", ) search_fields = ("spectacle__title", "participant__user__username", diff --git a/bda/models.py b/bda/models.py index 405236b9..b0dd8aeb 100644 --- a/bda/models.py +++ b/bda/models.py @@ -179,7 +179,8 @@ class Attribution(models.Model): given = models.BooleanField("Donnée", default=False) def __str__(self): - return "%s -- %s" % (self.participant, self.spectacle) + return "%s -- %s, %s" % (self.participant.user, self.spectacle.title, + self.spectacle.date) @python_2_unicode_compatible @@ -192,13 +193,14 @@ class SpectacleRevente(models.Model): def get_expiration_time(self): remaining_time = (self.attribution.spectacle.date - self.date) delay = max(datetime.timedelta(hours=2), - min(remaining_time/2, datetime.timedelta(days=2))) + min(remaining_time//2, datetime.timedelta(days=2))) return self.date + delay expiration_time = property(get_expiration_time) def get_shotgun(self): - return timezone.now > self.expiration_time + return timezone.now() > self.expiration_time shotgun = property(get_shotgun) def __str__(self): - return "%s" % self.attribution + return "%s -- %s" % (self.attribution.participant.user, + self.attribution.spectacle.title) From ca39dc813bfaee8842346ca684cf390abcf83f74 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sat, 23 Jul 2016 22:21:50 +0200 Subject: [PATCH 03/42] migration --- bda/migrations/0006_spectaclerevente.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 bda/migrations/0006_spectaclerevente.py diff --git a/bda/migrations/0006_spectaclerevente.py b/bda/migrations/0006_spectaclerevente.py new file mode 100644 index 00000000..a1bf12f6 --- /dev/null +++ b/bda/migrations/0006_spectaclerevente.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('bda', '0005_encoding'), + ] + + operations = [ + migrations.CreateModel( + name='SpectacleRevente', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('date', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Date de mise en vente')), + ('sold', models.BooleanField(default=False, verbose_name='Vendue')), + ('attribution', models.OneToOneField(to='bda.Attribution')), + ], + ), + ] From 2aaf9f681ec103a9c1c1f934058670af3a92bdf2 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sat, 23 Jul 2016 22:22:17 +0200 Subject: [PATCH 04/42] interface de revente de places --- bda/templates/bda-revente.html | 37 ++++++++++++++++++---------------- bda/views.py | 25 +++++++++++++++-------- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/bda/templates/bda-revente.html b/bda/templates/bda-revente.html index c901a7d1..9527a28b 100644 --- a/bda/templates/bda-revente.html +++ b/bda/templates/bda-revente.html @@ -1,26 +1,29 @@ {% extends "base_title.html" %} {% load staticfiles %} -{% block extra_head %} - -{% endblock %} - {% block realcontent %}

Revente de place

-
+{% if no_resell %} +

Places non revendues

+ {% csrf_token %} -{% if form.spectacle.errors %}
  • Sélectionnez un spetacle
{% endif %} -

-

-Bonjour,
-
-Je souhaite revendre {{ form.count }} place(s) pour {{ form.spectacle }}.
-Contactez-moi par email si vous êtes intéressé !
-
-{{ user.get_full_name }} ({{ user.email }}) -
-

- +
    + {% for attribution in no_resell %} +
  1. {{attribution.spectacle}}
  2. + {% endfor %} +
+ +
+{%endif%} +

Places en cours de revente

+
+ {% csrf_token %} +
    + {% for attribution in resell %} +
  1. {{attribution.spectacle}}
  2. + {% endfor %} +
+
{% endblock %} diff --git a/bda/views.py b/bda/views.py index fbf75359..ab445052 100644 --- a/bda/views.py +++ b/bda/views.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals from django.shortcuts import render, get_object_or_404 from django.contrib.auth.decorators import login_required +from django.contrib import messages from django.db import models from django.db.models import Count from django.core import serializers @@ -21,10 +22,10 @@ import time from gestioncof.decorators import cof_required, buro_required from bda.models import Spectacle, Participant, ChoixSpectacle, Attribution,\ - Tirage, render_template + Tirage, render_template, SpectacleRevente from bda.algorithm import Algorithm -from bda.forms import BaseBdaFormSet, TokenForm, ResellForm +from bda.forms import BaseBdaFormSet, TokenForm @cof_required @@ -302,13 +303,21 @@ def revente(request, tirage_id): 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) + for attr_id in request.POST.getlist('resell'): + attribution = Attribution.objects.get(id=int(attr_id)) + revente = SpectacleRevente(attribution=attribution) + revente.save() + for attr_id in request.POST.getlist('annul'): + revente = SpectacleRevente.objects.get(attribution__pk=attr_id) + revente.delete() + + attributions = participant.attribution_set.filter( + spectacle__date__gte=timezone.now) + resell = attributions.filter(spectaclerevente__isnull=False) + no_resell = attributions.filter(spectaclerevente__isnull=True) return render(request, "bda-revente.html", - {"form": form, 'tirage': tirage}) + {"participant": participant, 'tirage': tirage, + "resell": resell, "no_resell": no_resell}) @buro_required From 8f7b036fbcc8135b9c7681ef111776b7e5fc42b8 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 24 Jul 2016 00:48:05 +0200 Subject: [PATCH 05/42] annulations reventes --- bda/models.py | 9 +++++++-- bda/templates/bda-revente.html | 8 ++++++-- bda/views.py | 10 +++++----- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/bda/models.py b/bda/models.py index b0dd8aeb..1e80b248 100644 --- a/bda/models.py +++ b/bda/models.py @@ -185,7 +185,8 @@ class Attribution(models.Model): @python_2_unicode_compatible class SpectacleRevente(models.Model): - attribution = models.OneToOneField(Attribution) + attribution = models.OneToOneField(Attribution, + related_name="revente") date = models.DateTimeField("Date de mise en vente", default=timezone.now) sold = models.BooleanField("Vendue", default=False) @@ -194,13 +195,17 @@ class SpectacleRevente(models.Model): remaining_time = (self.attribution.spectacle.date - self.date) delay = max(datetime.timedelta(hours=2), min(remaining_time//2, datetime.timedelta(days=2))) - return self.date + delay + return self.date + delay + datetime.timedelta(hours=1) expiration_time = property(get_expiration_time) def get_shotgun(self): return timezone.now() > self.expiration_time shotgun = property(get_shotgun) + def get_cancellable(self): + return timezone.now() < self.date + datetime.timedelta(hours=1) + cancellable = property(get_cancellable) + def __str__(self): return "%s -- %s" % (self.attribution.participant.user, self.attribution.spectacle.title) diff --git a/bda/templates/bda-revente.html b/bda/templates/bda-revente.html index 9527a28b..16fbc0e2 100644 --- a/bda/templates/bda-revente.html +++ b/bda/templates/bda-revente.html @@ -16,14 +16,18 @@ {%endif%} +{% if resell %}

Places en cours de revente

{% csrf_token %}
    {% for attribution in resell %} -
  1. {{attribution.spectacle}}
  2. + + {% csrf_token %} + +
  3. {{attribution.spectacle}} {% if attribution.revente.cancellable %}{%endif%}
  4. {% endfor %}
-
+{% endif %} {% endblock %} diff --git a/bda/views.py b/bda/views.py index ab445052..82c09b55 100644 --- a/bda/views.py +++ b/bda/views.py @@ -6,7 +6,6 @@ from __future__ import unicode_literals from django.shortcuts import render, get_object_or_404 from django.contrib.auth.decorators import login_required -from django.contrib import messages from django.db import models from django.db.models import Count from django.core import serializers @@ -307,14 +306,15 @@ def revente(request, tirage_id): attribution = Attribution.objects.get(id=int(attr_id)) revente = SpectacleRevente(attribution=attribution) revente.save() - for attr_id in request.POST.getlist('annul'): - revente = SpectacleRevente.objects.get(attribution__pk=attr_id) + if 'annul' in request.POST: + revente = SpectacleRevente.objects\ + .get(attribution__pk=request.POST['annul']) revente.delete() attributions = participant.attribution_set.filter( spectacle__date__gte=timezone.now) - resell = attributions.filter(spectaclerevente__isnull=False) - no_resell = attributions.filter(spectaclerevente__isnull=True) + resell = attributions.filter(revente__isnull=False) + no_resell = attributions.filter(revente__isnull=True) return render(request, "bda-revente.html", {"participant": participant, 'tirage': tirage, "resell": resell, "no_resell": no_resell}) From 49afda933a9c3724501da264a7a6c9ead2b0b609 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 25 Jul 2016 02:51:19 +0200 Subject: [PATCH 06/42] model changes --- bda/admin.py | 3 +-- bda/models.py | 7 ++++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/bda/admin.py b/bda/admin.py index 05947534..2b24dbb7 100644 --- a/bda/admin.py +++ b/bda/admin.py @@ -213,10 +213,9 @@ class SpectacleReventeAdmin(admin.ModelAdmin): def participant(self, obj): return obj.attribution.participant - list_display = ("spectacle", "participant", "date", "sold") + list_display = ("spectacle", "participant", "date", "soldTo") raw_id_fields = ("attribution",) readonly_fields = ("shotgun", "expiration_time") - list_filter = ("sold", ) search_fields = ("spectacle__title", "participant__user__username", "participant__user__firstname", diff --git a/bda/models.py b/bda/models.py index 1e80b248..77b7d00c 100644 --- a/bda/models.py +++ b/bda/models.py @@ -132,6 +132,8 @@ class Participant(models.Model): max_length=6, choices=PAYMENT_TYPES, blank=True) tirage = models.ForeignKey(Tirage) + choicesrevente = models.ManyToManyField(Spectacle, + related_name="revente") def __str__(self): return "%s - %s" % (self.user, self.tirage.title) @@ -189,7 +191,10 @@ class SpectacleRevente(models.Model): related_name="revente") date = models.DateTimeField("Date de mise en vente", default=timezone.now) - sold = models.BooleanField("Vendue", default=False) + interested = models.ManyToManyField(Participant, + related_name="wanted", + blank=True, null=True) + soldTo = models.ForeignKey(Participant, blank=True, null=True) def get_expiration_time(self): remaining_time = (self.attribution.spectacle.date - self.date) From 7f87bfe8e206331445cd8f49f8c9492f1a6bf3f0 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 25 Jul 2016 02:51:45 +0200 Subject: [PATCH 07/42] new urls for revente --- bda/urls.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bda/urls.py b/bda/urls.py index 4e5811a1..747b49ce 100644 --- a/bda/urls.py +++ b/bda/urls.py @@ -38,4 +38,10 @@ urlpatterns = patterns( "bda.views.unpaid", name="bda-unpaid"), url(r'^mails-rappel/(?P\d+)$', "bda.views.send_rappel"), + url(r'^liste-revente/(?P\d+)$', + "bda.views.list_revente", + name="bda-liste-revente"), + url(r'^buy-revente/(?P\d+)$', + "bda.views.buy_revente", + name="bda-buy-revente"), ) From 78096bd6d0164398374f159228f74798a2a9c78a Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 25 Jul 2016 02:52:49 +0200 Subject: [PATCH 08/42] achat des reventes au shotgun --- bda/templates/liste-reventes.html | 23 +++++++++++++++ bda/templates/revente-confirm.html | 14 +++++++++ bda/views.py | 46 ++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 bda/templates/liste-reventes.html create mode 100644 bda/templates/revente-confirm.html diff --git a/bda/templates/liste-reventes.html b/bda/templates/liste-reventes.html new file mode 100644 index 00000000..6ae20388 --- /dev/null +++ b/bda/templates/liste-reventes.html @@ -0,0 +1,23 @@ +{% extends "base_title.html" %} +{% load staticfiles %} + +{% block realcontent %} +

Inscriptions pour BDA-Revente

+
+ {% csrf_token %} +
    + {% for spectacle in spectacles %} +
  • {{spectacle}}
  • + {%endfor%} +
+ +
+ + {% if shotgun %} +

Places disponibles immédiatement

+
    + {% for spectacle in shotgun %} +
  • {{spectacle}}
  • + {% endfor %} + {% endif %} +{% endblock %} diff --git a/bda/templates/revente-confirm.html b/bda/templates/revente-confirm.html new file mode 100644 index 00000000..d1a348aa --- /dev/null +++ b/bda/templates/revente-confirm.html @@ -0,0 +1,14 @@ +{% extends "base_title.html" %} +{% load staticfiles %} + +{# TODO: ajouter template du mail ? Changer formulation #} +{%block realcontent %} +Vous êtes sur le point de racheter une place pour {{spectacle.title}} à {{selected.attribution.participant.user.get_full_name}} ; confirmez-vous ceci ? Un mail lui sera envoyé pour le/la notifier et vous permettre d'entrer en contact. + +
    +{% csrf_token %} + + +
    + +{%endblock%} diff --git a/bda/views.py b/bda/views.py index 82c09b55..3c42f94d 100644 --- a/bda/views.py +++ b/bda/views.py @@ -4,6 +4,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 @@ -320,6 +322,50 @@ def revente(request, tirage_id): "resell": resell, "no_resell": no_resell}) +@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.all() + shotgun = [] + for spectacle in spectacles: + spectacle.am_interested = spectacle.revente.filter( + pk=participant.id).exists() + revente_objects = SpectacleRevente.objects.filter( + attribution__spectacle=spectacle) + 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) + return render(request, "liste-reventes.html", + {"participant": participant, 'tirage': tirage, + "spectacles": spectacles, 'shotgun': shotgun}) + + +@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) + if request.POST: + revente = SpectacleRevente.objects.get(pk=request.POST['id']) + revente.soldTo = participant + revente.save() + + if reventes.exists(): + idx = random.randint(0, reventes.count() - 1) + selected = reventes.all()[idx] + return render(request, "revente-confirm.html", + {"selected": selected, "spectacle": spectacle}) + + @buro_required def spectacle(request, tirage_id, spectacle_id): tirage = get_object_or_404(Tirage, id=tirage_id) From 92dd9a18a252b5cb4f256b5a8c9f9307538d34a5 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 25 Jul 2016 02:54:04 +0200 Subject: [PATCH 09/42] migrations --- bda/migrations/0006_revente.py | 38 +++++++++++++++++++++++++ bda/migrations/0006_spectaclerevente.py | 24 ---------------- 2 files changed, 38 insertions(+), 24 deletions(-) create mode 100644 bda/migrations/0006_revente.py delete mode 100644 bda/migrations/0006_spectaclerevente.py diff --git a/bda/migrations/0006_revente.py b/bda/migrations/0006_revente.py new file mode 100644 index 00000000..8aa7c0f8 --- /dev/null +++ b/bda/migrations/0006_revente.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('bda', '0005_encoding'), + ] + + operations = [ + migrations.CreateModel( + name='SpectacleRevente', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('date', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Date de mise en vente')), + ('attribution', models.OneToOneField(related_name='revente', to='bda.Attribution')), + ], + ), + migrations.AddField( + model_name='participant', + name='choicesrevente', + field=models.ManyToManyField(related_name='revente', to='bda.Spectacle'), + ), + migrations.AddField( + model_name='spectaclerevente', + name='interested', + field=models.ManyToManyField(related_name='wanted', to='bda.Participant'), + ), + migrations.AddField( + model_name='spectaclerevente', + name='soldTo', + field=models.ForeignKey(blank=True, to='bda.Participant', null=True), + ), + ] diff --git a/bda/migrations/0006_spectaclerevente.py b/bda/migrations/0006_spectaclerevente.py deleted file mode 100644 index a1bf12f6..00000000 --- a/bda/migrations/0006_spectaclerevente.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models -import django.utils.timezone - - -class Migration(migrations.Migration): - - dependencies = [ - ('bda', '0005_encoding'), - ] - - operations = [ - migrations.CreateModel( - name='SpectacleRevente', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('date', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Date de mise en vente')), - ('sold', models.BooleanField(default=False, verbose_name='Vendue')), - ('attribution', models.OneToOneField(to='bda.Attribution')), - ], - ), - ] From 90581af528639ab6f3824f578f9bfa8176795ab2 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 25 Jul 2016 03:01:12 +0200 Subject: [PATCH 10/42] ajoute lien sur la page d'accueil --- gestioncof/templates/home.html | 1 + 1 file changed, 1 insertion(+) diff --git a/gestioncof/templates/home.html b/gestioncof/templates/home.html index ed567455..015e835f 100644 --- a/gestioncof/templates/home.html +++ b/gestioncof/templates/home.html @@ -33,6 +33,7 @@
  • État des demandes
  • Mes places
  • Revendre une place
  • +
  • Inscription à BDA-Revente

{% endfor %} From d12a21d44c3d9155db7100cceb5f5f5ac20247cb Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 25 Jul 2016 23:03:33 +0200 Subject: [PATCH 11/42] use forms --- bda/forms.py | 13 ++++++++----- bda/models.py | 2 +- bda/templates/bda-revente.html | 22 +--------------------- bda/views.py | 28 +++++++++++----------------- 4 files changed, 21 insertions(+), 44 deletions(-) diff --git a/bda/forms.py b/bda/forms.py index 9f5e3890..b1acf3d9 100644 --- a/bda/forms.py +++ b/bda/forms.py @@ -6,7 +6,8 @@ from __future__ import unicode_literals from django import forms from django.forms.models import BaseInlineFormSet -from bda.models import Spectacle +from django.utils import timezone +from bda.models import Attribution class BaseBdaFormSet(BaseInlineFormSet): @@ -42,10 +43,12 @@ class SpectacleModelChoiceField(forms.ModelChoiceField): class ResellForm(forms.Form): - count = forms.ChoiceField(choices=(("1", "1"), ("2", "2"),)) - spectacle = SpectacleModelChoiceField(queryset=Spectacle.objects.none()) + attributions = forms.ModelMultipleChoiceField( + queryset=Attribution.objects.none(), + widget=forms.CheckboxSelectMultiple) def __init__(self, participant, *args, **kwargs): super(ResellForm, self).__init__(*args, **kwargs) - self.fields['spectacle'].queryset = participant.attributions.all() \ - .distinct() + self.fields['attributions'].queryset = participant.attribution_set\ + .filter(spectacle__date__gte=timezone.now(), + revente__isnull=True) diff --git a/bda/models.py b/bda/models.py index 77b7d00c..91af3e99 100644 --- a/bda/models.py +++ b/bda/models.py @@ -193,7 +193,7 @@ class SpectacleRevente(models.Model): default=timezone.now) interested = models.ManyToManyField(Participant, related_name="wanted", - blank=True, null=True) + blank=True) soldTo = models.ForeignKey(Participant, blank=True, null=True) def get_expiration_time(self): diff --git a/bda/templates/bda-revente.html b/bda/templates/bda-revente.html index 16fbc0e2..0b6e14a6 100644 --- a/bda/templates/bda-revente.html +++ b/bda/templates/bda-revente.html @@ -4,30 +4,10 @@ {% block realcontent %}

Revente de place

-{% if no_resell %}

Places non revendues

{% csrf_token %} -
    - {% for attribution in no_resell %} -
  1. {{attribution.spectacle}}
  2. - {% endfor %} -
+ {{form}}
-{%endif%} -{% if resell %} -

Places en cours de revente

-
- {% csrf_token %} -
    - {% for attribution in resell %} - - {% csrf_token %} - -
  1. {{attribution.spectacle}} {% if attribution.revente.cancellable %}{%endif%}
  2. - {% endfor %} -
-
-{% endif %} {% endblock %} diff --git a/bda/views.py b/bda/views.py index 3c42f94d..4c31a635 100644 --- a/bda/views.py +++ b/bda/views.py @@ -26,7 +26,7 @@ from bda.models import Spectacle, Participant, ChoixSpectacle, Attribution,\ Tirage, render_template, SpectacleRevente from bda.algorithm import Algorithm -from bda.forms import BaseBdaFormSet, TokenForm +from bda.forms import BaseBdaFormSet, TokenForm, ResellForm @cof_required @@ -303,23 +303,17 @@ def revente(request, tirage_id): user=request.user, tirage=tirage) if not participant.paid: return render(request, "bda-notpaid.html", {}) - if request.POST: - for attr_id in request.POST.getlist('resell'): - attribution = Attribution.objects.get(id=int(attr_id)) - revente = SpectacleRevente(attribution=attribution) - revente.save() - if 'annul' in request.POST: - revente = SpectacleRevente.objects\ - .get(attribution__pk=request.POST['annul']) - revente.delete() - - attributions = participant.attribution_set.filter( - spectacle__date__gte=timezone.now) - resell = attributions.filter(revente__isnull=False) - no_resell = attributions.filter(revente__isnull=True) + if request.method == 'POST': + form = ResellForm(participant, request.POST) + if form.is_valid(): + attributions = form.cleaned_data["attributions"] + for attribution in attributions: + revente = SpectacleRevente(attribution=attribution) + revente.save() + else: + form = ResellForm(participant) return render(request, "bda-revente.html", - {"participant": participant, 'tirage': tirage, - "resell": resell, "no_resell": no_resell}) + {'tirage': tirage, "form": form}) @login_required From 460a135fa591126f9b92c8cfccfc7a3378ef9b58 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Wed, 27 Jul 2016 13:08:00 +0200 Subject: [PATCH 12/42] use forms --- bda/forms.py | 40 +++++++++++++++++---- bda/templates/bda-revente.html | 17 +++++++-- bda/templates/liste-reventes.html | 6 +--- bda/views.py | 58 +++++++++++++++++++++++-------- 4 files changed, 94 insertions(+), 27 deletions(-) diff --git a/bda/forms.py b/bda/forms.py index b1acf3d9..60fdb0fd 100644 --- a/bda/forms.py +++ b/bda/forms.py @@ -4,10 +4,12 @@ from __future__ import division from __future__ import print_function from __future__ import unicode_literals +from datetime import timedelta + from django import forms from django.forms.models import BaseInlineFormSet from django.utils import timezone -from bda.models import Attribution +from bda.models import Attribution, Spectacle class BaseBdaFormSet(BaseInlineFormSet): @@ -36,19 +38,45 @@ class TokenForm(forms.Form): token = forms.CharField(widget=forms.widgets.Textarea()) -class SpectacleModelChoiceField(forms.ModelChoiceField): +class AttributionModelMultipleChoiceField(forms.ModelMultipleChoiceField): def label_from_instance(self, obj): - return "%s le %s (%s) à %.02f€" % (obj.title, obj.date_no_seconds(), - obj.location, obj.price) + return "%s" % obj.spectacle class ResellForm(forms.Form): - attributions = forms.ModelMultipleChoiceField( + attributions = AttributionModelMultipleChoiceField( queryset=Attribution.objects.none(), - widget=forms.CheckboxSelectMultiple) + widget=forms.CheckboxSelectMultiple, + required=False) def __init__(self, participant, *args, **kwargs): super(ResellForm, self).__init__(*args, **kwargs) self.fields['attributions'].queryset = participant.attribution_set\ .filter(spectacle__date__gte=timezone.now(), revente__isnull=True) + + +class AnnulForm(forms.Form): + attributions = AttributionModelMultipleChoiceField( + queryset=Attribution.objects.none(), + widget=forms.CheckboxSelectMultiple, + required=False) + + def __init__(self, participant, *args, **kwargs): + super(AnnulForm, self).__init__(*args, **kwargs) + self.fields['attributions'].queryset = participant.attribution_set\ + .filter(spectacle__date__gte=timezone.now(), + revente__isnull=False, + revente__date__gte=timezone.now()-timedelta(hours=1)) + + +class InscriptionReventeForm(forms.Form): + spectacles = forms.ModelMultipleChoiceField( + queryset=Spectacle.objects.none(), + widget=forms.CheckboxSelectMultiple, + required=False) + + def __init__(self, tirage, *args, **kwargs): + super(InscriptionReventeForm, self).__init__(*args, **kwargs) + self.fields['spectacles'].queryset = tirage.spectacle_set.filter( + date__gte=timezone.now()) diff --git a/bda/templates/bda-revente.html b/bda/templates/bda-revente.html index 0b6e14a6..e7fd6e6d 100644 --- a/bda/templates/bda-revente.html +++ b/bda/templates/bda-revente.html @@ -7,7 +7,20 @@

Places non revendues

{% csrf_token %} - {{form}} - + {{resellform}} +
+ +

Places en cours de revente

+
+ {% csrf_token %} +
    + {% for box in annulform.attributions %} +
  • {{box.choice_label}}{{box.tag}}
  • + {% endfor %} + {% for attrib in overdue %} +
  • {{attrib.spectacle}}
  • + {%endfor%} +
+ {% endblock %} diff --git a/bda/templates/liste-reventes.html b/bda/templates/liste-reventes.html index 6ae20388..4b48825f 100644 --- a/bda/templates/liste-reventes.html +++ b/bda/templates/liste-reventes.html @@ -5,11 +5,7 @@

Inscriptions pour BDA-Revente

{% csrf_token %} -
    - {% for spectacle in spectacles %} -
  • {{spectacle}}
  • - {%endfor%} -
+ {{form}}
diff --git a/bda/views.py b/bda/views.py index 4c31a635..fe180ada 100644 --- a/bda/views.py +++ b/bda/views.py @@ -26,7 +26,8 @@ from bda.models import Spectacle, Participant, ChoixSpectacle, Attribution,\ Tirage, render_template, SpectacleRevente from bda.algorithm import Algorithm -from bda.forms import BaseBdaFormSet, TokenForm, ResellForm +from bda.forms import BaseBdaFormSet, TokenForm, ResellForm, AnnulForm,\ + InscriptionReventeForm @cof_required @@ -304,16 +305,35 @@ def revente(request, tirage_id): if not participant.paid: return render(request, "bda-notpaid.html", {}) if request.method == 'POST': - form = ResellForm(participant, request.POST) - if form.is_valid(): - attributions = form.cleaned_data["attributions"] - for attribution in attributions: - revente = SpectacleRevente(attribution=attribution) - revente.save() + if 'resell' in request.POST: + resellform = ResellForm(participant, request.POST, prefix='resell') + annulform = AnnulForm(participant, prefix='annul') + if resellform.is_valid(): + attributions = resellform.cleaned_data["attributions"] + for attribution in attributions: + revente = SpectacleRevente(attribution=attribution) + revente.save() + 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() + else: + resellform = ResellForm(participant, prefix='resell') + annulform = AnnulForm(participant, prefix='annul') else: - form = ResellForm(participant) + resellform = ResellForm(participant, prefix='resell') + annulform = AnnulForm(participant, prefix='annul') + overdue = participant.attribution_set.filter( + spectacle__date__gte=timezone.now(), + revente__isnull=False, + revente__date__lte=timezone.now()-timedelta(hours=1)) + return render(request, "bda-revente.html", - {'tirage': tirage, "form": form}) + {'tirage': tirage, 'overdue': overdue, + "annulform": annulform, "resellform": resellform}) @login_required @@ -321,11 +341,10 @@ 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.all() + spectacles = tirage.spectacle_set.filter( + date__gte=timezone.now()) shotgun = [] for spectacle in spectacles: - spectacle.am_interested = spectacle.revente.filter( - pk=participant.id).exists() revente_objects = SpectacleRevente.objects.filter( attribution__spectacle=spectacle) revente_count = 0 @@ -335,9 +354,20 @@ def list_revente(request, tirage_id): 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() + else: + form = InscriptionReventeForm( + tirage, + initial={'spectacles': participant.choicesrevente.all()}) + return render(request, "liste-reventes.html", - {"participant": participant, 'tirage': tirage, - "spectacles": spectacles, 'shotgun': shotgun}) + {"form": form, 'shotgun': shotgun}) @login_required From d4198d16d733fd3788fb3d6dfa955e3930d92a23 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Wed, 27 Jul 2016 23:37:48 +0200 Subject: [PATCH 13/42] migration --- bda/migrations/0007_auto_20160727_2336.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 bda/migrations/0007_auto_20160727_2336.py diff --git a/bda/migrations/0007_auto_20160727_2336.py b/bda/migrations/0007_auto_20160727_2336.py new file mode 100644 index 00000000..c8b4674a --- /dev/null +++ b/bda/migrations/0007_auto_20160727_2336.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('bda', '0006_revente'), + ] + + operations = [ + migrations.AlterField( + model_name='spectaclerevente', + name='interested', + field=models.ManyToManyField(related_name='wanted', to='bda.Participant', blank=True), + ), + ] From 5b18eace20bf241b2d9d8dae0ff4b2ea0ad3bd2b Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Fri, 29 Jul 2016 01:50:08 +0200 Subject: [PATCH 14/42] send mail --- bda/models.py | 24 ++++++++++++++++++++---- bda/templates/mail-revente.txt | 8 ++++++++ cof/settings_dev.py | 3 +++ 3 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 bda/templates/mail-revente.txt diff --git a/bda/models.py b/bda/models.py index 91af3e99..d1d3ba7e 100644 --- a/bda/models.py +++ b/bda/models.py @@ -207,10 +207,26 @@ class SpectacleRevente(models.Model): return timezone.now() > self.expiration_time shotgun = property(get_shotgun) - def get_cancellable(self): - return timezone.now() < self.date + datetime.timedelta(hours=1) - cancellable = property(get_cancellable) - def __str__(self): return "%s -- %s" % (self.attribution.participant.user, self.attribution.spectacle.title) + + def send_notif(self): + # On récupère la liste des inscrits + inscrits = self.attribution.spectacle.revente.select_related('user') + + mails_to_send = [] + mail_object = "%s" % (self.attribution.spectacle) + for participant in inscrits: + mail_body = render_template('mail-revente.txt', { + 'user': participant.user, + 'spectacle': self.spectacle, + 'revente': self}) + mail_tot = mail.EmailMessage( + mail_object, mail_body, + settings.REVENTE_FROM, [participant.email], + [], headers={'Reply-To': settings.REVENTE_REPLY_TO}) + mails_to_send.append(mail_tot) + + connection = mail.get_connection() + connection.send_messages(mails_to_send) diff --git a/bda/templates/mail-revente.txt b/bda/templates/mail-revente.txt new file mode 100644 index 00000000..16655696 --- /dev/null +++ b/bda/templates/mail-revente.txt @@ -0,0 +1,8 @@ +Bonjour {{ user.get_full_name }} + +Une place pour le spectacle {{ spectacle.title }} ({{spectacle.date_no_seconds}}) a été postée sur BDA-Revente. + +Si ce spectacle t'intéresse toujours, merci de nous le signaler en cliquant sur ce lien : {% url "bda-inscr-revente" revente.id %}. Dans le cas où plusieurs personnes seraient intéressées, nous procèderons à un tirage au sort le {{revente.date_no_seconds}} + +Chaleureusement, +Le BDA diff --git a/cof/settings_dev.py b/cof/settings_dev.py index 502cb0ee..0666ee9f 100644 --- a/cof/settings_dev.py +++ b/cof/settings_dev.py @@ -142,6 +142,9 @@ PETITS_COURS_REPLYTO = "cof@ens.fr" RAPPEL_FROM = 'Le BdA ' RAPPEL_REPLY_TO = RAPPEL_FROM +REVENTE_FROM = 'BDA-Revente ' +REVENTE_REPLY_TO = REVENTE_FROM + LOGIN_URL = "/login" LOGIN_REDIRECT_URL = "/" From 32d98faf0da1aae43c7c6c7b0bc86cd31ab07940 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sat, 3 Sep 2016 05:21:27 +0200 Subject: [PATCH 15/42] minor fixes --- bda/models.py | 3 ++- bda/views.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/bda/models.py b/bda/models.py index 90ca9449..7856b517 100644 --- a/bda/models.py +++ b/bda/models.py @@ -158,7 +158,8 @@ class Participant(models.Model): blank=True) tirage = models.ForeignKey(Tirage) choicesrevente = models.ManyToManyField(Spectacle, - related_name="revente") + related_name="revente", + blank=True) def __str__(self): return "%s - %s" % (self.user, self.tirage.title) diff --git a/bda/views.py b/bda/views.py index 0c48ab6c..1ae1b425 100644 --- a/bda/views.py +++ b/bda/views.py @@ -20,6 +20,7 @@ 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,\ From c26bb5c30925b123fbe734034936883275ef5a39 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sat, 3 Sep 2016 05:22:41 +0200 Subject: [PATCH 16/42] adds bda-revente to home --- gestioncof/templates/home.html | 1 + 1 file changed, 1 insertion(+) diff --git a/gestioncof/templates/home.html b/gestioncof/templates/home.html index 0907adc6..0cd32ebf 100644 --- a/gestioncof/templates/home.html +++ b/gestioncof/templates/home.html @@ -43,6 +43,7 @@ {% else %}
  • Mes places
  • Revendre une place
  • +
  • S'inscrire à BDA-Revente
  • {% endif %} {% endfor %} From a32278d76520d84dbe6ab5092f5b56ef5b7b8d9f Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sat, 3 Sep 2016 05:23:05 +0200 Subject: [PATCH 17/42] styling forms --- bda/templates/bda-revente.html | 47 +++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/bda/templates/bda-revente.html b/bda/templates/bda-revente.html index fcad757b..61bf5452 100644 --- a/bda/templates/bda-revente.html +++ b/bda/templates/bda-revente.html @@ -1,26 +1,55 @@ {% extends "base_title.html" %} -{% load staticfiles %} +{% load bootstrap %} {% block realcontent %}

    Revente de place

    Places non revendues

    -
    + {% csrf_token %} - {{resellform}} - +
    +
    +
      + {% for box in resellform.attributions %} +
    • + {{box.tag}} + {{box.choice_label}} +
    • + {% endfor %} +
    +
    +
    +
    + +
    - +

    Places en cours de revente

    {% csrf_token %} -
      - {% for box in annulform.attributions %} -
    • {{box.choice_label}}{{box.tag}}
    • - {% endfor %} +
      +
      +
        + {% for box in annulform.attributions %} +
      • + {{box.tag}} + {{box.choice_label}} +
      • + {% endfor %} + {% for attrib in overdue %} +
      • + + {{attrib.spectacle}} +
      • + {% endfor %} +
      +
      +
      + {{annulform | bootstrap}} {% for attrib in overdue %}
    • {{attrib.spectacle}}
    • {%endfor%}
    +
    {% endblock %} From 798f5226020012cfdcb1b5f2e84da3edca03460e Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sat, 3 Sep 2016 05:25:01 +0200 Subject: [PATCH 18/42] migration --- bda/migrations/0008_revente.py | 38 ++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 bda/migrations/0008_revente.py diff --git a/bda/migrations/0008_revente.py b/bda/migrations/0008_revente.py new file mode 100644 index 00000000..4db2c9de --- /dev/null +++ b/bda/migrations/0008_revente.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('bda', '0007_extends_spectacle'), + ] + + operations = [ + migrations.CreateModel( + name='SpectacleRevente', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('date', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Date de mise en vente')), + ('attribution', models.OneToOneField(related_name='revente', to='bda.Attribution')), + ], + ), + migrations.AddField( + model_name='participant', + name='choicesrevente', + field=models.ManyToManyField(related_name='revente', to='bda.Spectacle', blank=True), + ), + migrations.AddField( + model_name='spectaclerevente', + name='interested', + field=models.ManyToManyField(related_name='wanted', to='bda.Participant', blank=True), + ), + migrations.AddField( + model_name='spectaclerevente', + name='soldTo', + field=models.ForeignKey(blank=True, to='bda.Participant', null=True), + ), + ] From 026e585eb7622fa46b9b7c7741175876908afa65 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sat, 3 Sep 2016 12:07:16 +0200 Subject: [PATCH 19/42] delete unused migrations --- bda/migrations/0006_revente.py | 38 ----------------------- bda/migrations/0007_auto_20160727_2336.py | 19 ------------ 2 files changed, 57 deletions(-) delete mode 100644 bda/migrations/0006_revente.py delete mode 100644 bda/migrations/0007_auto_20160727_2336.py diff --git a/bda/migrations/0006_revente.py b/bda/migrations/0006_revente.py deleted file mode 100644 index 8aa7c0f8..00000000 --- a/bda/migrations/0006_revente.py +++ /dev/null @@ -1,38 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models -import django.utils.timezone - - -class Migration(migrations.Migration): - - dependencies = [ - ('bda', '0005_encoding'), - ] - - operations = [ - migrations.CreateModel( - name='SpectacleRevente', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('date', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Date de mise en vente')), - ('attribution', models.OneToOneField(related_name='revente', to='bda.Attribution')), - ], - ), - migrations.AddField( - model_name='participant', - name='choicesrevente', - field=models.ManyToManyField(related_name='revente', to='bda.Spectacle'), - ), - migrations.AddField( - model_name='spectaclerevente', - name='interested', - field=models.ManyToManyField(related_name='wanted', to='bda.Participant'), - ), - migrations.AddField( - model_name='spectaclerevente', - name='soldTo', - field=models.ForeignKey(blank=True, to='bda.Participant', null=True), - ), - ] diff --git a/bda/migrations/0007_auto_20160727_2336.py b/bda/migrations/0007_auto_20160727_2336.py deleted file mode 100644 index c8b4674a..00000000 --- a/bda/migrations/0007_auto_20160727_2336.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('bda', '0006_revente'), - ] - - operations = [ - migrations.AlterField( - model_name='spectaclerevente', - name='interested', - field=models.ManyToManyField(related_name='wanted', to='bda.Participant', blank=True), - ), - ] From 285e3cb78f77445412f9d91023e346e6717279e9 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sat, 3 Sep 2016 18:46:56 +0200 Subject: [PATCH 20/42] styling forms --- bda/templates/bda-revente.html | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/bda/templates/bda-revente.html b/bda/templates/bda-revente.html index 61bf5452..0a5cffb3 100644 --- a/bda/templates/bda-revente.html +++ b/bda/templates/bda-revente.html @@ -45,11 +45,6 @@ - {{annulform | bootstrap}} - {% for attrib in overdue %} -
  • {{attrib.spectacle}}
  • - {%endfor%} - - + {% endblock %} From 46f91adc08ad9dfafa882ce900055712876695f2 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sat, 3 Sep 2016 18:47:38 +0200 Subject: [PATCH 21/42] revente des places au shotgun --- bda/templates/bda-success.html | 8 ++----- bda/templates/liste-reventes.html | 11 +++++----- bda/templates/revente-confirm.html | 15 ++++++++----- bda/views.py | 34 ++++++++++++++++++++++++------ 4 files changed, 45 insertions(+), 23 deletions(-) diff --git a/bda/templates/bda-success.html b/bda/templates/bda-success.html index ebcc87a7..5e970eb7 100644 --- a/bda/templates/bda-success.html +++ b/bda/templates/bda-success.html @@ -1,12 +1,8 @@ {% extends "base_title.html" %} {% load staticfiles %} -{% block extra_head %} - -{% endblock %} - {% block realcontent %} -

    Revente de place

    -

    Votre offre de revente de {{ places }} pour {{ show.title }} le {{ show.date_no_seconds }} ({{ show.location }}) à {{ show.price }}€ a bien été envoyée.

    +

    Revente de place

    +

    Un mail a bien été envoyé à {{seller.get_full_name}} ({{seller.email}}), pour racheter une place pour {{spectacle.title}} !

    {% endblock %} diff --git a/bda/templates/liste-reventes.html b/bda/templates/liste-reventes.html index 4b48825f..9e4acb92 100644 --- a/bda/templates/liste-reventes.html +++ b/bda/templates/liste-reventes.html @@ -1,16 +1,17 @@ {% extends "base_title.html" %} -{% load staticfiles %} +{% load bootstrap %} {% block realcontent %}

    Inscriptions pour BDA-Revente

    -
    + {% csrf_token %} - {{form}} - + {{form | bootstrap}} +
    {% if shotgun %} -

    Places disponibles immédiatement

    +
    +

    Places disponibles immédiatement

      {% for spectacle in shotgun %}
    • {{spectacle}}
    • diff --git a/bda/templates/revente-confirm.html b/bda/templates/revente-confirm.html index d1a348aa..60cac8b8 100644 --- a/bda/templates/revente-confirm.html +++ b/bda/templates/revente-confirm.html @@ -1,14 +1,19 @@ {% extends "base_title.html" %} {% load staticfiles %} -{# TODO: ajouter template du mail ? Changer formulation #} {%block realcontent %} -Vous êtes sur le point de racheter une place pour {{spectacle.title}} à {{selected.attribution.participant.user.get_full_name}} ; confirmez-vous ceci ? Un mail lui sera envoyé pour le/la notifier et vous permettre d'entrer en contact. +

      Rachat d'une place

      +Note : ce mail sera envoyé à une personne au hasard revendant sa place. +
      +Bonjour !
       
      +Je souhaiterais racheter ta place pour {{spectacle.title}} le {{spectacle.date}} ({{spectacle.location}}) à {{spectacle.price}}€. 
      +Contacte-moi si tu es toujours intéressé-e !
      +
      +{{user.get_full_name}} ({{user.email}})
      +
      {% csrf_token %} - - +
      - {%endblock%} diff --git a/bda/views.py b/bda/views.py index 1ae1b425..40bd924d 100644 --- a/bda/views.py +++ b/bda/views.py @@ -323,7 +323,8 @@ def list_revente(request, tirage_id): shotgun = [] for spectacle in spectacles: revente_objects = SpectacleRevente.objects.filter( - attribution__spectacle=spectacle) + attribution__spectacle=spectacle, + soldTo__isnull=True) revente_count = 0 for revente in revente_objects: if revente.shotgun: @@ -354,17 +355,36 @@ def buy_revente(request, spectacle_id): participant, created = Participant.objects.get_or_create( user=request.user, tirage=tirage) reventes = SpectacleRevente.objects.filter( - attribution__spectacle=spectacle) + attribution__spectacle=spectacle, + soldTo__isnull=True) + + if not reventes.exists(): + return render(request, "bda-no-revente.html", {}) + if request.POST: - revente = SpectacleRevente.objects.get(pk=request.POST['id']) + idx = random.randint(0, reventes.count() - 1) + revente = reventes.all()[idx] revente.soldTo = participant revente.save() + mail = """Bonjour ! + +Je souhaiterais racheter ta place pour %s le %s (%s) à %.02f€. +Contacte-moi si tu es toujours intéressé·e ! + +%s (%s)""" % (spectacle.title, spectacle.date_no_seconds(), + spectacle.location, spectacle.price, + request.user.get_full_name(), request.user.email) + send_mail("BdA-Revente : %s" % spectacle.title, mail, + request.user.email, + [revente.attribution.participant.user.email], + fail_silently=False) + return render(request, "bda-success.html", + {"seller": revente.attribution.participant.user, + "spectacle": spectacle}) - if reventes.exists(): - idx = random.randint(0, reventes.count() - 1) - selected = reventes.all()[idx] return render(request, "revente-confirm.html", - {"selected": selected, "spectacle": spectacle}) + {"spectacle": spectacle, + "user": request.user}) @buro_required From a607f353429cead4a5af4d452edcd6b15091cc50 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sat, 3 Sep 2016 19:39:28 +0200 Subject: [PATCH 22/42] =?UTF-8?q?inscription=20des=20d=C3=A9=C3=A7us=20?= =?UTF-8?q?=C3=A0=20BdA-Revente?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bda/views.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bda/views.py b/bda/views.py index 40bd924d..3641b776 100644 --- a/bda/views.py +++ b/bda/views.py @@ -235,6 +235,11 @@ def do_tirage(request, tirage_id): 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) From e9e0be7960cf7c7e5f48d2f18e6f8486efe0de0d Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 4 Sep 2016 11:14:09 +0200 Subject: [PATCH 23/42] =?UTF-8?q?g=C3=A8re=20les=20places=20demand=C3=A9es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bda/models.py | 32 +++++++++++++++++++++++++++++++ bda/templates/bda-interested.html | 9 +++++++++ bda/urls.py | 3 +++ bda/views.py | 16 +++++++++++++++- 4 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 bda/templates/bda-interested.html diff --git a/bda/models.py b/bda/models.py index 7856b517..c3621a45 100644 --- a/bda/models.py +++ b/bda/models.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import calendar import datetime +import random from django.db import models from django.contrib.auth.models import User @@ -256,3 +257,34 @@ class SpectacleRevente(models.Model): connection = mail.get_connection() connection.send_messages(mails_to_send) + + def tirage(self): + inscrits = self.interested + spectacle = self.attribution.spectacle + user = self.attribution.participant.user + if inscrits.exists(): + idx = random.randint(0, inscrits.count() - 1) + winner = inscrits.all()[idx] + self.soldTo = winner + mail_buyer = """Bonjour, + +Tu as été tiré-e au sort pour racheter une place pour %s le %s (%s) à %0.02f€. +Tu peux contacter le vendeur à l'adresse %s. + +Chaleureusement, +Le BdA""" % (spectacle.title, spectacle.date_no_seconds(), + spectacle.location, spectacle.price, user.email) + + mail.send_mail("BdA-Revente : %s" % spectacle.title, + mail_buyer, "bda@ens.fr", [winner.user.email], + fail_silently=False) + mail_seller = """Bonjour, +La personne tirée au sort pour racheter ta place pour %s est %s. +Tu peux le/la contacter à l'adresse %s. + +Chaleureusement, +Le BdA""" % (spectacle.title, winner.user.get_full_name(), winner.user.email) + + mail.send_mail("BdA-Revente : %s" % spectacle.title, + mail_seller, "bda@ens.fr", [user.email], + fail_silently=False) diff --git a/bda/templates/bda-interested.html b/bda/templates/bda-interested.html new file mode 100644 index 00000000..acfb1d1e --- /dev/null +++ b/bda/templates/bda-interested.html @@ -0,0 +1,9 @@ +{% extends "base_title.html" %} +{% load staticfiles %} + +{% block realcontent %} +

      Inscription à une revente

      +

      Votre inscription pour a bien été enregistrée !

      +

      Le tirage au sort pour cette revente ({{spectacle}}) sera effectué le {{date}}. + +{% endblock %} diff --git a/bda/urls.py b/bda/urls.py index 13a163f4..94b797fa 100644 --- a/bda/urls.py +++ b/bda/urls.py @@ -38,6 +38,9 @@ urlpatterns = [ url(r'^buy-revente/(?P\d+)$', "bda.views.buy_revente", name="bda-buy-revente"), + url(r'^revente-interested/(?P\d+)$', + "bda.views.revente_interested", + name='bda-revente-interested'), url(r'^mails-rappel/(?P\d+)$', views.send_rappel), url(r'^descriptions/(?P\d+)$', views.descriptions_spectacles, name='bda-descriptions'), diff --git a/bda/views.py b/bda/views.py index 3641b776..a3fd9168 100644 --- a/bda/views.py +++ b/bda/views.py @@ -318,6 +318,20 @@ def revente(request, tirage_id): "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.interested.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) @@ -373,7 +387,7 @@ def buy_revente(request, spectacle_id): revente.save() mail = """Bonjour ! -Je souhaiterais racheter ta place pour %s le %s (%s) à %.02f€. +Je souhaiterais racheter ta place pour %s le %s (%s) à %.02f€. Contacte-moi si tu es toujours intéressé·e ! %s (%s)""" % (spectacle.title, spectacle.date_no_seconds(), From 0b40ebb6f75d15f5fafcd06c48f2a926461a81b0 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 4 Sep 2016 11:14:53 +0200 Subject: [PATCH 24/42] corrections de templates --- bda/templates/bda-no-revente.html | 6 ++++++ bda/templates/mail-revente.txt | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 bda/templates/bda-no-revente.html diff --git a/bda/templates/bda-no-revente.html b/bda/templates/bda-no-revente.html new file mode 100644 index 00000000..eabb3dc7 --- /dev/null +++ b/bda/templates/bda-no-revente.html @@ -0,0 +1,6 @@ +{% extends "base_title.html" %} + +{% block realcontent %} +

      BdA-Revente

      +

      Il n'y a plus de places en revente pour ce spectacle, désolé !

      +{% endblock %} diff --git a/bda/templates/mail-revente.txt b/bda/templates/mail-revente.txt index 16655696..adc61eb9 100644 --- a/bda/templates/mail-revente.txt +++ b/bda/templates/mail-revente.txt @@ -1,8 +1,8 @@ Bonjour {{ user.get_full_name }} -Une place pour le spectacle {{ spectacle.title }} ({{spectacle.date_no_seconds}}) a été postée sur BDA-Revente. +Une place pour le spectacle {{ spectacle.title }} ({{spectacle.date_no_seconds}}) a été postée sur BdA-Revente. -Si ce spectacle t'intéresse toujours, merci de nous le signaler en cliquant sur ce lien : {% url "bda-inscr-revente" revente.id %}. Dans le cas où plusieurs personnes seraient intéressées, nous procèderons à un tirage au sort le {{revente.date_no_seconds}} +Si ce spectacle t'intéresse toujours, merci de nous le signaler en cliquant sur ce lien : {% url "bda-interested-revente" revente.id %}. Dans le cas où plusieurs personnes seraient intéressées, nous procèderons à un tirage au sort le {{revente.date_no_seconds}} Chaleureusement, -Le BDA +Le BdA From 3bc9880db1f0c92bd29669d81ab03185715d0c23 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 5 Sep 2016 02:29:49 +0200 Subject: [PATCH 25/42] revente de revente + confirmation de transfert de places --- bda/forms.py | 7 +++-- bda/models.py | 10 ++++--- bda/templates/bda-revente.html | 23 +++++++++++++++ bda/views.py | 53 ++++++++++++++++++++++++++++++---- 4 files changed, 80 insertions(+), 13 deletions(-) diff --git a/bda/forms.py b/bda/forms.py index 60fdb0fd..0c886e0f 100644 --- a/bda/forms.py +++ b/bda/forms.py @@ -52,8 +52,8 @@ class ResellForm(forms.Form): def __init__(self, participant, *args, **kwargs): super(ResellForm, self).__init__(*args, **kwargs) self.fields['attributions'].queryset = participant.attribution_set\ - .filter(spectacle__date__gte=timezone.now(), - revente__isnull=True) + .filter(spectacle__date__gte=timezone.now())\ + .exclude(revente__seller=participant) class AnnulForm(forms.Form): @@ -67,7 +67,8 @@ class AnnulForm(forms.Form): self.fields['attributions'].queryset = participant.attribution_set\ .filter(spectacle__date__gte=timezone.now(), revente__isnull=False, - revente__date__gte=timezone.now()-timedelta(hours=1)) + revente__date__gte=timezone.now()-timedelta(hours=1), + revente__seller=participant) class InscriptionReventeForm(forms.Form): diff --git a/bda/models.py b/bda/models.py index c3621a45..428dbfb7 100644 --- a/bda/models.py +++ b/bda/models.py @@ -221,6 +221,8 @@ class SpectacleRevente(models.Model): interested = models.ManyToManyField(Participant, related_name="wanted", blank=True) + seller = models.ForeignKey(Participant, + related_name="original_shows") soldTo = models.ForeignKey(Participant, blank=True, null=True) def get_expiration_time(self): @@ -235,7 +237,7 @@ class SpectacleRevente(models.Model): shotgun = property(get_shotgun) def __str__(self): - return "%s -- %s" % (self.attribution.participant.user, + return "%s -- %s" % (self.seller, self.attribution.spectacle.title) def send_notif(self): @@ -261,7 +263,7 @@ class SpectacleRevente(models.Model): def tirage(self): inscrits = self.interested spectacle = self.attribution.spectacle - user = self.attribution.participant.user + seller = self.seller if inscrits.exists(): idx = random.randint(0, inscrits.count() - 1) winner = inscrits.all()[idx] @@ -273,7 +275,7 @@ Tu peux contacter le vendeur à l'adresse %s. Chaleureusement, Le BdA""" % (spectacle.title, spectacle.date_no_seconds(), - spectacle.location, spectacle.price, user.email) + spectacle.location, spectacle.price, seller.email) mail.send_mail("BdA-Revente : %s" % spectacle.title, mail_buyer, "bda@ens.fr", [winner.user.email], @@ -286,5 +288,5 @@ Chaleureusement, Le BdA""" % (spectacle.title, winner.user.get_full_name(), winner.user.email) mail.send_mail("BdA-Revente : %s" % spectacle.title, - mail_seller, "bda@ens.fr", [user.email], + mail_seller, "bda@ens.fr", [seller.email], fail_silently=False) diff --git a/bda/templates/bda-revente.html b/bda/templates/bda-revente.html index 0a5cffb3..a5f64678 100644 --- a/bda/templates/bda-revente.html +++ b/bda/templates/bda-revente.html @@ -24,6 +24,7 @@
      +{% if annulform.attributions or overdue %}

      Places en cours de revente

      {% csrf_token %} @@ -45,6 +46,28 @@
    + {% if annulform.attributions %} + {% endif %} +{% endif %} +
    +{% if sold %} +

    Places revendues

    + + {% for attrib in sold %} + + + {% csrf_token %} + + + + + + + {% endfor %} +
    {{attrib.spectacle}}{{attrib.revente.soldTo.user.get_full_name}}
    +{% endif %} {% endblock %} diff --git a/bda/views.py b/bda/views.py index a3fd9168..8c92f4ab 100644 --- a/bda/views.py +++ b/bda/views.py @@ -9,7 +9,7 @@ import random from django.shortcuts import render, get_object_or_404 from django.contrib.auth.decorators import login_required from django.db import models -from django.db.models import Count +from django.db.models import Count, Q from django.core import serializers from django.forms.models import inlineformset_factory from django.http import HttpResponseBadRequest @@ -293,8 +293,14 @@ def revente(request, tirage_id): if resellform.is_valid(): attributions = resellform.cleaned_data["attributions"] for attribution in attributions: - revente = SpectacleRevente(attribution=attribution) + revente, created = SpectacleRevente.objects.get_or_create( + attribution=attribution, + defaults={'seller': participant}) + if not created: + revente.seller = participant + revente.date = timezone.now() revente.save() + elif 'annul' in request.POST: annulform = AnnulForm(participant, request.POST, prefix='annul') resellform = ResellForm(participant, prefix='resell') @@ -302,19 +308,54 @@ def revente(request, tirage_id): 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['transfer'][0] + rev = SpectacleRevente.objects.filter(soldTo__isnull=False, + id=revente_id) + if rev.exists(): + revente = rev.get() + revente.date = timezone.now() - timedelta(hours=1) + revente.soldTo = None + revente.interested = None + # schedule job + 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__date__lte=timezone.now()-timedelta(hours=1)) + 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, + {'tirage': tirage, 'overdue': overdue, "sold": sold, "annulform": annulform, "resellform": resellform}) @@ -395,10 +436,10 @@ Contacte-moi si tu es toujours intéressé·e ! request.user.get_full_name(), request.user.email) send_mail("BdA-Revente : %s" % spectacle.title, mail, request.user.email, - [revente.attribution.participant.user.email], + [revente.seller.user.email], fail_silently=False) return render(request, "bda-success.html", - {"seller": revente.attribution.participant.user, + {"seller": revente.seller.user, "spectacle": spectacle}) return render(request, "revente-confirm.html", From de522a0035443ab93cdb93533b53c7b3f7c8b8bd Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 5 Sep 2016 02:38:46 +0200 Subject: [PATCH 26/42] fixes + deprecated migration --- bda/forms.py | 6 +++-- bda/migrations/0008_revente.py | 38 ------------------------------- bda/templates/liste-reventes.html | 2 +- 3 files changed, 5 insertions(+), 41 deletions(-) delete mode 100644 bda/migrations/0008_revente.py diff --git a/bda/forms.py b/bda/forms.py index 0c886e0f..c2eec894 100644 --- a/bda/forms.py +++ b/bda/forms.py @@ -8,6 +8,7 @@ from datetime import timedelta from django import forms from django.forms.models import BaseInlineFormSet +from django.db.models import Q from django.utils import timezone from bda.models import Attribution, Spectacle @@ -67,8 +68,9 @@ class AnnulForm(forms.Form): self.fields['attributions'].queryset = participant.attribution_set\ .filter(spectacle__date__gte=timezone.now(), revente__isnull=False, - revente__date__gte=timezone.now()-timedelta(hours=1), - revente__seller=participant) + revente__date__gte=timezone.now()-timedelta(hours=1))\ + .filter(Q(revente__soldTo__isnull=True) | + Q(revente__soldTo=participant)) class InscriptionReventeForm(forms.Form): diff --git a/bda/migrations/0008_revente.py b/bda/migrations/0008_revente.py deleted file mode 100644 index 4db2c9de..00000000 --- a/bda/migrations/0008_revente.py +++ /dev/null @@ -1,38 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -import django.utils.timezone - - -class Migration(migrations.Migration): - - dependencies = [ - ('bda', '0007_extends_spectacle'), - ] - - operations = [ - migrations.CreateModel( - name='SpectacleRevente', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('date', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Date de mise en vente')), - ('attribution', models.OneToOneField(related_name='revente', to='bda.Attribution')), - ], - ), - migrations.AddField( - model_name='participant', - name='choicesrevente', - field=models.ManyToManyField(related_name='revente', to='bda.Spectacle', blank=True), - ), - migrations.AddField( - model_name='spectaclerevente', - name='interested', - field=models.ManyToManyField(related_name='wanted', to='bda.Participant', blank=True), - ), - migrations.AddField( - model_name='spectaclerevente', - name='soldTo', - field=models.ForeignKey(blank=True, to='bda.Participant', null=True), - ), - ] diff --git a/bda/templates/liste-reventes.html b/bda/templates/liste-reventes.html index 9e4acb92..1920256f 100644 --- a/bda/templates/liste-reventes.html +++ b/bda/templates/liste-reventes.html @@ -12,7 +12,7 @@ {% if shotgun %}

    Places disponibles immédiatement

    -
      +
        {% for spectacle in shotgun %}
      • {{spectacle}}
      • {% endfor %} From b6655047ed8791cbe467b2a2b5033b1ac42fab87 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 5 Sep 2016 03:10:06 +0200 Subject: [PATCH 27/42] verbose names --- bda/admin.py | 11 ++++------- bda/models.py | 9 +++++++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/bda/admin.py b/bda/admin.py index b604d65b..37fdf9c7 100644 --- a/bda/admin.py +++ b/bda/admin.py @@ -216,16 +216,13 @@ class SpectacleReventeAdmin(admin.ModelAdmin): def spectacle(self, obj): return obj.attribution.spectacle - def participant(self, obj): - return obj.attribution.participant - - list_display = ("spectacle", "participant", "date", "soldTo") + list_display = ("spectacle", "seller", "date", "soldTo") raw_id_fields = ("attribution",) readonly_fields = ("shotgun", "expiration_time") search_fields = ("spectacle__title", - "participant__user__username", - "participant__user__firstname", - "participant__user__lastname",) + "seller__user__username", + "seller__user__firstname", + "seller__user__lastname",) admin.site.register(CategorieSpectacle) diff --git a/bda/models.py b/bda/models.py index 428dbfb7..baed138e 100644 --- a/bda/models.py +++ b/bda/models.py @@ -222,8 +222,10 @@ class SpectacleRevente(models.Model): related_name="wanted", blank=True) seller = models.ForeignKey(Participant, - related_name="original_shows") - soldTo = models.ForeignKey(Participant, blank=True, null=True) + related_name="original_shows", + verbose_name="Vendeur") + soldTo = models.ForeignKey(Participant, blank=True, null=True, + verbose_name="Vendue à") def get_expiration_time(self): remaining_time = (self.attribution.spectacle.date - self.date) @@ -240,6 +242,9 @@ class SpectacleRevente(models.Model): return "%s -- %s" % (self.seller, self.attribution.spectacle.title) + class Meta: + verbose_name = "Revente" + def send_notif(self): # On récupère la liste des inscrits inscrits = self.attribution.spectacle.revente.select_related('user') From f0553d709e6388211477f6afecf8d00f9fbce9bb Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 5 Sep 2016 03:32:29 +0200 Subject: [PATCH 28/42] check lors de l'inscription pour des places en revente --- bda/templates/liste-reventes.html | 3 +++ bda/views.py | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/bda/templates/liste-reventes.html b/bda/templates/liste-reventes.html index 1920256f..2a5ddc95 100644 --- a/bda/templates/liste-reventes.html +++ b/bda/templates/liste-reventes.html @@ -3,6 +3,9 @@ {% block realcontent %}

        Inscriptions pour BDA-Revente

        + {% if deja_revente %} +

        Des reventes existent déjà pour certains de ces spectacles ; vérifie les places disponibles sans tirage !

        + {% endif %}
        {% csrf_token %} {{form | bootstrap}} diff --git a/bda/views.py b/bda/views.py index 8c92f4ab..dc1f4cb2 100644 --- a/bda/views.py +++ b/bda/views.py @@ -381,6 +381,7 @@ def list_revente(request, tirage_id): spectacles = tirage.spectacle_set.filter( date__gte=timezone.now()) shotgun = [] + deja_revente = False for spectacle in spectacles: revente_objects = SpectacleRevente.objects.filter( attribution__spectacle=spectacle, @@ -399,13 +400,25 @@ def list_revente(request, tirage_id): 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 not revente.shotgun: + revente.interested.add(participant) + break + deja_revente = True + else: form = InscriptionReventeForm( tirage, initial={'spectacles': participant.choicesrevente.all()}) return render(request, "liste-reventes.html", - {"form": form, 'shotgun': shotgun}) + {"form": form, 'shotgun': shotgun, + "deja_revente": deja_revente}) @login_required From 2a56f8e255b86e6a9bdc0fc89d3b097a48e9d894 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 11 Sep 2016 13:32:38 +0200 Subject: [PATCH 29/42] buy more than one place --- bda/forms.py | 8 ++++++++ bda/views.py | 29 ++++++++++++++++------------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/bda/forms.py b/bda/forms.py index c2eec894..f50c496b 100644 --- a/bda/forms.py +++ b/bda/forms.py @@ -44,6 +44,14 @@ class AttributionModelMultipleChoiceField(forms.ModelMultipleChoiceField): return "%s" % obj.spectacle +class BuyResellForm(forms.Form): + num = forms.ChoiceField(choices=[]) + + def __init__(self, spectacle, *args, **kwargs): + super(BuyResellForm, self).__init__(*args, **kwargs) + self.fields['num'].choices = range(1, spectacle.revente.count()) + + class ResellForm(forms.Form): attributions = AttributionModelMultipleChoiceField( queryset=Attribution.objects.none(), diff --git a/bda/views.py b/bda/views.py index dc1f4cb2..7b3b30e9 100644 --- a/bda/views.py +++ b/bda/views.py @@ -28,7 +28,7 @@ from bda.models import Spectacle, Participant, ChoixSpectacle, Attribution,\ from bda.algorithm import Algorithm from bda.forms import BaseBdaFormSet, TokenForm, ResellForm, AnnulForm,\ - InscriptionReventeForm + InscriptionReventeForm, BuyResellForm @cof_required @@ -435,11 +435,14 @@ def buy_revente(request, spectacle_id): return render(request, "bda-no-revente.html", {}) if request.POST: - idx = random.randint(0, reventes.count() - 1) - revente = reventes.all()[idx] - revente.soldTo = participant - revente.save() - mail = """Bonjour ! + form = BuyResellForm(spectacle, request.POST) + if form.is_valid(): + num = form.cleaned_data['num'] + reventes = random.sample(reventes.all(), num) + for revente in reventes: + revente.soldTo = participant + revente.save() + mail = """Bonjour ! Je souhaiterais racheter ta place pour %s le %s (%s) à %.02f€. Contacte-moi si tu es toujours intéressé·e ! @@ -447,13 +450,13 @@ Contacte-moi si tu es toujours intéressé·e ! %s (%s)""" % (spectacle.title, spectacle.date_no_seconds(), spectacle.location, spectacle.price, request.user.get_full_name(), request.user.email) - 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.seller.user, - "spectacle": spectacle}) + 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.seller.user, + "spectacle": spectacle}) return render(request, "revente-confirm.html", {"spectacle": spectacle, From f67933891072a91cecd8e9d918155ea5fd3befb2 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 19 Sep 2016 16:08:12 +0200 Subject: [PATCH 30/42] scrap buying many resells --- bda/forms.py | 8 -------- bda/models.py | 3 +-- bda/templates/revente-confirm.html | 7 ++++--- bda/views.py | 28 ++++++++++++---------------- 4 files changed, 17 insertions(+), 29 deletions(-) diff --git a/bda/forms.py b/bda/forms.py index f50c496b..c2eec894 100644 --- a/bda/forms.py +++ b/bda/forms.py @@ -44,14 +44,6 @@ class AttributionModelMultipleChoiceField(forms.ModelMultipleChoiceField): return "%s" % obj.spectacle -class BuyResellForm(forms.Form): - num = forms.ChoiceField(choices=[]) - - def __init__(self, spectacle, *args, **kwargs): - super(BuyResellForm, self).__init__(*args, **kwargs) - self.fields['num'].choices = range(1, spectacle.revente.count()) - - class ResellForm(forms.Form): attributions = AttributionModelMultipleChoiceField( queryset=Attribution.objects.none(), diff --git a/bda/models.py b/bda/models.py index ad9df179..9b467f9c 100644 --- a/bda/models.py +++ b/bda/models.py @@ -272,8 +272,7 @@ class SpectacleRevente(models.Model): spectacle = self.attribution.spectacle seller = self.seller if inscrits.exists(): - idx = random.randint(0, inscrits.count() - 1) - winner = inscrits.all()[idx] + winner = random.choice(inscrits.all()) self.soldTo = winner mail_buyer = """Bonjour, diff --git a/bda/templates/revente-confirm.html b/bda/templates/revente-confirm.html index 60cac8b8..d7614c25 100644 --- a/bda/templates/revente-confirm.html +++ b/bda/templates/revente-confirm.html @@ -1,9 +1,11 @@ {% extends "base_title.html" %} {% load staticfiles %} + {%block realcontent %}

        Rachat d'une place

        -Note : ce mail sera envoyé à une personne au hasard revendant sa place. + +{% csrf_token %}
         Bonjour !
         
        @@ -12,8 +14,7 @@ Contacte-moi si tu es toujours intéressé-e !
         
         {{user.get_full_name}} ({{user.email}})
         
        - -{% csrf_token %} +

        Note : ce mail sera envoyé à une personne au hasard revendant sa place.

        {%endblock%} diff --git a/bda/views.py b/bda/views.py index 7b3b30e9..7ea26df4 100644 --- a/bda/views.py +++ b/bda/views.py @@ -28,7 +28,7 @@ from bda.models import Spectacle, Participant, ChoixSpectacle, Attribution,\ from bda.algorithm import Algorithm from bda.forms import BaseBdaFormSet, TokenForm, ResellForm, AnnulForm,\ - InscriptionReventeForm, BuyResellForm + InscriptionReventeForm @cof_required @@ -435,14 +435,10 @@ def buy_revente(request, spectacle_id): return render(request, "bda-no-revente.html", {}) if request.POST: - form = BuyResellForm(spectacle, request.POST) - if form.is_valid(): - num = form.cleaned_data['num'] - reventes = random.sample(reventes.all(), num) - for revente in reventes: - revente.soldTo = participant - revente.save() - mail = """Bonjour ! + revente = random.choice(reventes.all(), 1) + revente.soldTo = participant + revente.save() + mail = """Bonjour ! Je souhaiterais racheter ta place pour %s le %s (%s) à %.02f€. Contacte-moi si tu es toujours intéressé·e ! @@ -450,13 +446,13 @@ Contacte-moi si tu es toujours intéressé·e ! %s (%s)""" % (spectacle.title, spectacle.date_no_seconds(), spectacle.location, spectacle.price, request.user.get_full_name(), request.user.email) - 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.seller.user, - "spectacle": spectacle}) + 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.participant.user, + "spectacle": spectacle}) return render(request, "revente-confirm.html", {"spectacle": spectacle, From 051a979a9baecf2b46b35fb14e800ec43f448178 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Wed, 21 Sep 2016 15:30:41 +0200 Subject: [PATCH 31/42] added cron management --- bda/management/commands/manage_reventes.py | 32 ++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 bda/management/commands/manage_reventes.py diff --git a/bda/management/commands/manage_reventes.py b/bda/management/commands/manage_reventes.py new file mode 100644 index 00000000..9fea3f6a --- /dev/null +++ b/bda/management/commands/manage_reventes.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- + +from __future__ import unicode_literals + +from django.core.management import BaseCommand +from django.utils import timezone +from datetime import timedelta +from bda.models import SpectacleRevente + + +class Command(BaseCommand): + help = "Envoie les mails de notification et effectue " \ + "les tirages au sort des reventes" + + def handle(self, *args, **options): + now = timezone.now() + reventes = SpectacleRevente.objects.all() + for revente in reventes: + # Check si < 24h + if (revente.attribution.spectacle.date <= + revente.date + timedelta(days=1)) and \ + now >= revente.date + timedelta(minutes=15) and \ + not revente.notif_sent: + revente.mail_shotgun() + # Check si délai de retrait dépassé + elif (now >= revente.date + timedelta(hours=1) and + not revente.notif_sent): + revente.send_notif() + # Check si tirage à faire + elif (now >= revente.expiration_time and + not revente.tirage_done): + revente.tirage() From 6b63f0f30f6bab9e5938c0ee9511dbf867ef7792 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Wed, 21 Sep 2016 15:39:01 +0200 Subject: [PATCH 32/42] end --- bda/models.py | 51 ++++++++++++++++++++++++++++------ bda/templates/mail-shotgun.txt | 8 ++++++ bda/views.py | 11 ++++++-- 3 files changed, 59 insertions(+), 11 deletions(-) create mode 100644 bda/templates/mail-shotgun.txt diff --git a/bda/models.py b/bda/models.py index 9b467f9c..b97db2c2 100644 --- a/bda/models.py +++ b/bda/models.py @@ -5,8 +5,8 @@ from __future__ import print_function from __future__ import unicode_literals import calendar -import datetime import random +from datetime import timedelta from django.db import models from django.contrib.auth.models import User @@ -229,15 +229,29 @@ class SpectacleRevente(models.Model): soldTo = models.ForeignKey(Participant, blank=True, null=True, verbose_name="Vendue à") + notif_sent = models.BooleanField("Notification envoyée", + default=False) + tirage_done = models.BooleanField("Tirage effectué", + default=False) + def get_expiration_time(self): - remaining_time = (self.attribution.spectacle.date - self.date) - delay = max(datetime.timedelta(hours=2), - min(remaining_time//2, datetime.timedelta(days=2))) - return self.date + delay + datetime.timedelta(hours=1) + # L'acheteur doit être connu au plus 12h avant le spectacle + remaining_time = (self.attribution.spectacle.date + - self.date - timedelta(hours=13)) + # Au minimum, on attend 2 jours avant le tirage + delay = min(remaining_time, timedelta(days=2)) + # On a aussi 1h pour changer d'avis + return self.date + delay + timedelta(hours=1) expiration_time = property(get_expiration_time) def get_shotgun(self): - return timezone.now() > self.expiration_time + # 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)) shotgun = property(get_shotgun) def __str__(self): @@ -248,7 +262,6 @@ class SpectacleRevente(models.Model): verbose_name = "Revente" def send_notif(self): - # On récupère la liste des inscrits inscrits = self.attribution.spectacle.revente.select_related('user') mails_to_send = [] @@ -260,12 +273,33 @@ class SpectacleRevente(models.Model): 'revente': self}) mail_tot = mail.EmailMessage( mail_object, mail_body, - settings.REVENTE_FROM, [participant.email], + settings.REVENTE_FROM, [participant.user.email], [], headers={'Reply-To': settings.REVENTE_REPLY_TO}) mails_to_send.append(mail_tot) connection = mail.get_connection() connection.send_messages(mails_to_send) + self.notif_sent = True + + def mail_shotgun(self): + inscrits = self.attribution.spectacle.revente.select_related('user') + + mails_to_send = [] + mail_object = "%s" % (self.attribution.spectacle) + for participant in inscrits: + mail_body = render_template('mail-shotgun.txt', { + 'user': participant.user, + 'spectacle': self.spectacle, + 'mail': self.attribution.participant.user.email}) + mail_tot = mail.EmailMessage( + mail_object, mail_body, + settings.REVENTE_FROM, [participant.user.email], + [], headers={'Reply-To': settings.REVENTE_REPLY_TO}) + mails_to_send.append(mail_tot) + + connection = mail.get_connection() + connection.send_messages(mails_to_send) + self.notif_sent = True def tirage(self): inscrits = self.interested @@ -296,3 +330,4 @@ Le BdA""" % (spectacle.title, winner.user.get_full_name(), winner.user.email) mail.send_mail("BdA-Revente : %s" % spectacle.title, mail_seller, "bda@ens.fr", [seller.email], fail_silently=False) + self.tirage_done = True diff --git a/bda/templates/mail-shotgun.txt b/bda/templates/mail-shotgun.txt new file mode 100644 index 00000000..8c4e7bd9 --- /dev/null +++ b/bda/templates/mail-shotgun.txt @@ -0,0 +1,8 @@ +Bonjour {{ user.get_full_name }} + +Une place pour le spectacle {{ spectacle.title }} ({{spectacle.date_no_seconds}}) a été postée sur BdA-Revente. + +Puisque ce spectacle a lieu dans moins de 24h, il n'y a pas de tirage au sort pour cette place : elle est disponible immédiatement à l'addresse {{url "bda-buy-revente" spectacle.id}}, à la disposition de tous. + +Chaleureusement, +Le BdA diff --git a/bda/views.py b/bda/views.py index 7ea26df4..3b36379f 100644 --- a/bda/views.py +++ b/bda/views.py @@ -12,7 +12,8 @@ from django.db import models from django.db.models import Count, Q from django.core import serializers from django.forms.models import inlineformset_factory -from django.http import HttpResponseBadRequest +from django.http import HttpResponseBadRequest, HttpResponseRedirect +from django.core.urlresolvers import reverse import hashlib from django.core.mail import send_mail @@ -325,7 +326,7 @@ def revente(request, tirage_id): elif 'reinit' in request.POST: resellform = ResellForm(participant, prefix='resell') annulform = AnnulForm(participant, prefix='annul') - revente_id = request.POST['transfer'][0] + revente_id = request.POST['reinit'][0] rev = SpectacleRevente.objects.filter(soldTo__isnull=False, id=revente_id) if rev.exists(): @@ -333,7 +334,6 @@ def revente(request, tirage_id): revente.date = timezone.now() - timedelta(hours=1) revente.soldTo = None revente.interested = None - # schedule job else: resellform = ResellForm(participant, prefix='resell') @@ -430,6 +430,11 @@ def buy_revente(request, spectacle_id): 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-liste-revente", + args=[tirage.id])) if not reventes.exists(): return render(request, "bda-no-revente.html", {}) From 81d1bc27506943c198b77cd12db055bf00c8f31c Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Wed, 21 Sep 2016 15:40:31 +0200 Subject: [PATCH 33/42] migration --- bda/migrations/0009_revente.py | 48 ++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 bda/migrations/0009_revente.py diff --git a/bda/migrations/0009_revente.py b/bda/migrations/0009_revente.py new file mode 100644 index 00000000..258762c7 --- /dev/null +++ b/bda/migrations/0009_revente.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('bda', '0008_py3'), + ] + + operations = [ + migrations.CreateModel( + name='SpectacleRevente', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('date', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Date de mise en vente')), + ('notif_sent', models.BooleanField(default=False, verbose_name='Notification envoy\xe9e')), + ('tirage_done', models.BooleanField(default=False, verbose_name='Tirage effectu\xe9')), + ('attribution', models.OneToOneField(related_name='revente', to='bda.Attribution')), + ], + options={ + 'verbose_name': 'Revente', + }, + ), + migrations.AddField( + model_name='participant', + name='choicesrevente', + field=models.ManyToManyField(related_name='revente', to='bda.Spectacle', blank=True), + ), + migrations.AddField( + model_name='spectaclerevente', + name='interested', + field=models.ManyToManyField(related_name='wanted', to='bda.Participant', blank=True), + ), + migrations.AddField( + model_name='spectaclerevente', + name='seller', + field=models.ForeignKey(related_name='original_shows', verbose_name='Vendeur', to='bda.Participant'), + ), + migrations.AddField( + model_name='spectaclerevente', + name='soldTo', + field=models.ForeignKey(verbose_name='Vendue \xe0', blank=True, to='bda.Participant', null=True), + ), + ] From a5e6a8e635694e26cd209a9b4a286a7b1b4d6702 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 25 Sep 2016 12:12:31 +0200 Subject: [PATCH 34/42] fix message liste_revente --- bda/views.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bda/views.py b/bda/views.py index 3b36379f..241bc90d 100644 --- a/bda/views.py +++ b/bda/views.py @@ -406,11 +406,11 @@ def list_revente(request, tirage_id): if qset.exists(): # On l'inscrit à l'un des tirages au sort for revente in qset.all(): - if not revente.shotgun: + if revente.shotgun: + deja_revente = True + else: revente.interested.add(participant) break - deja_revente = True - else: form = InscriptionReventeForm( tirage, From bbfce33c3f9cb75cf69d4970cfd6524ac9b55950 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 25 Sep 2016 14:39:18 +0200 Subject: [PATCH 35/42] corrects bug with bda-buy --- bda/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bda/views.py b/bda/views.py index 241bc90d..bac7415d 100644 --- a/bda/views.py +++ b/bda/views.py @@ -440,7 +440,7 @@ def buy_revente(request, spectacle_id): return render(request, "bda-no-revente.html", {}) if request.POST: - revente = random.choice(reventes.all(), 1) + revente = random.choice(reventes.all()) revente.soldTo = participant revente.save() mail = """Bonjour ! @@ -456,7 +456,7 @@ Contacte-moi si tu es toujours intéressé·e ! [revente.seller.user.email], fail_silently=False) return render(request, "bda-success.html", - {"seller": revente.participant.user, + {"seller": revente.attribution.participant.user, "spectacle": spectacle}) return render(request, "revente-confirm.html", From 8a3f4b7431ed8588116cd8f602caa8cb8b87d851 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 25 Sep 2016 14:39:38 +0200 Subject: [PATCH 36/42] template fixes --- bda/templates/bda-notpaid.html | 2 +- bda/templates/bda-wrongtime.html | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 bda/templates/bda-wrongtime.html diff --git a/bda/templates/bda-notpaid.html b/bda/templates/bda-notpaid.html index b25c2ba3..0dd4e4df 100644 --- a/bda/templates/bda-notpaid.html +++ b/bda/templates/bda-notpaid.html @@ -1,6 +1,6 @@ {% extends "base_title.html" %} {% block realcontent %} -

        Nope

        +

        Nope

        Avant de revendre des places, il faut aller les payer !

        {% endblock %} diff --git a/bda/templates/bda-wrongtime.html b/bda/templates/bda-wrongtime.html new file mode 100644 index 00000000..5e17926b --- /dev/null +++ b/bda/templates/bda-wrongtime.html @@ -0,0 +1,6 @@ +{% extends "base_title.html" %} + +{% block realcontent %} +

        Nope

        +

        Cette revente n'est pas disponible actuellement, désolé !

        +{% endblock %} From 15e755334dde6a1ca56ee70ea1af1fba2d480c56 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 26 Sep 2016 15:31:09 +0200 Subject: [PATCH 37/42] cron --- bda/management/commands/manage_reventes.py | 4 ++++ provisioning/cron.dev | 1 + provisioning/cron.md | 11 +++++++++++ 3 files changed, 16 insertions(+) diff --git a/bda/management/commands/manage_reventes.py b/bda/management/commands/manage_reventes.py index 9fea3f6a..f45357b1 100644 --- a/bda/management/commands/manage_reventes.py +++ b/bda/management/commands/manage_reventes.py @@ -14,6 +14,7 @@ class Command(BaseCommand): def handle(self, *args, **options): now = timezone.now() + self.stdout.write(now) reventes = SpectacleRevente.objects.all() for revente in reventes: # Check si < 24h @@ -22,11 +23,14 @@ class Command(BaseCommand): now >= revente.date + timedelta(minutes=15) and \ not revente.notif_sent: revente.mail_shotgun() + self.stdout.write("Mail de disponibilité immédiate envoyé") # Check si délai de retrait dépassé elif (now >= revente.date + timedelta(hours=1) and not revente.notif_sent): revente.send_notif() + self.stdout.write("Mail d'inscription à une revente envoyé") # Check si tirage à faire elif (now >= revente.expiration_time and not revente.tirage_done): revente.tirage() + self.stdout.write("Tirage effectué, mails envoyés") diff --git a/provisioning/cron.dev b/provisioning/cron.dev index d249d547..6cd2ca81 100644 --- a/provisioning/cron.dev +++ b/provisioning/cron.dev @@ -7,3 +7,4 @@ DBNAME="cof_gestion" DBPASSWD="4KZt3nGPLVeWSvtBZPSM3fSzXpzEU4" 19 */12 * * * date >> /vagrant/rappels.log ; python /vagrant/manage.py sendrappels >> /vagrant/rappels.log 2>&1 +*/5 * * * * python /vagrant/manage.py manage_revente >> /vagrant/reventes.log 2>&1 diff --git a/provisioning/cron.md b/provisioning/cron.md index 8b3f608e..840a8716 100644 --- a/provisioning/cron.md +++ b/provisioning/cron.md @@ -14,3 +14,14 @@ envoyés). - Garde les logs peut être une bonne idée. Exemple : voir le fichier `provisioning/cron.dev`. + +## Gestion des mails de revente + +Il faut effectuer très régulièrement la commande `manage_reventes` de GestioCOF, +qui gère toutes les actions associées à BdA-Revente : envoi des mails de notification, +tirages. + +- Pour l'instant un délai de 5 min est hardcodé +- Garde des logs ; ils vont finir par être assez lourds si on a beaucoup de reventes. + +Exemple : provisioning/cron.dev From 59b8f406b652ffb1ddd9a20a726d3e5461264cf7 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 26 Sep 2016 15:53:58 +0200 Subject: [PATCH 38/42] fix bug shotgun --- bda/views.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/bda/views.py b/bda/views.py index bac7415d..12c57d49 100644 --- a/bda/views.py +++ b/bda/views.py @@ -406,7 +406,7 @@ def list_revente(request, tirage_id): if qset.exists(): # On l'inscrit à l'un des tirages au sort for revente in qset.all(): - if revente.shotgun: + if revente.shotgun and not revente.soldTo: deja_revente = True else: revente.interested.add(participant) @@ -435,12 +435,16 @@ def buy_revente(request, spectacle_id): revente.delete() return HttpResponseRedirect(reverse("bda-liste-revente", args=[tirage.id])) + reventes_shotgun = [] + for revente in reventes.all(): + if revente.shotgun: + reventes_shotgun.append(revente) - if not reventes.exists(): + if reventes_shotgun.empty(): return render(request, "bda-no-revente.html", {}) if request.POST: - revente = random.choice(reventes.all()) + revente = random.choice(reventes_shotgun) revente.soldTo = participant revente.save() mail = """Bonjour ! From 3943c3ab9ff39b363804695fa2b7209119266ed1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Mon, 26 Sep 2016 16:03:33 +0200 Subject: [PATCH 39/42] PEP8 --- bda/migrations/0009_revente.py | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/bda/migrations/0009_revente.py b/bda/migrations/0009_revente.py index 258762c7..0c039700 100644 --- a/bda/migrations/0009_revente.py +++ b/bda/migrations/0009_revente.py @@ -15,11 +15,17 @@ class Migration(migrations.Migration): migrations.CreateModel( name='SpectacleRevente', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('date', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Date de mise en vente')), - ('notif_sent', models.BooleanField(default=False, verbose_name='Notification envoy\xe9e')), - ('tirage_done', models.BooleanField(default=False, verbose_name='Tirage effectu\xe9')), - ('attribution', models.OneToOneField(related_name='revente', to='bda.Attribution')), + ('id', models.AutoField(verbose_name='ID', serialize=False, + auto_created=True, primary_key=True)), + ('date', models.DateTimeField( + default=django.utils.timezone.now, + verbose_name='Date de mise en vente')), + ('notif_sent', models.BooleanField( + default=False, verbose_name='Notification envoy\xe9e')), + ('tirage_done', models.BooleanField( + default=False, verbose_name='Tirage effectu\xe9')), + ('attribution', models.OneToOneField(related_name='revente', + to='bda.Attribution')), ], options={ 'verbose_name': 'Revente', @@ -28,21 +34,26 @@ class Migration(migrations.Migration): migrations.AddField( model_name='participant', name='choicesrevente', - field=models.ManyToManyField(related_name='revente', to='bda.Spectacle', blank=True), + field=models.ManyToManyField(related_name='revente', + to='bda.Spectacle', blank=True), ), migrations.AddField( model_name='spectaclerevente', name='interested', - field=models.ManyToManyField(related_name='wanted', to='bda.Participant', blank=True), + field=models.ManyToManyField(related_name='wanted', + to='bda.Participant', blank=True), ), migrations.AddField( model_name='spectaclerevente', name='seller', - field=models.ForeignKey(related_name='original_shows', verbose_name='Vendeur', to='bda.Participant'), + field=models.ForeignKey(related_name='original_shows', + verbose_name='Vendeur', + to='bda.Participant'), ), migrations.AddField( model_name='spectaclerevente', name='soldTo', - field=models.ForeignKey(verbose_name='Vendue \xe0', blank=True, to='bda.Participant', null=True), + field=models.ForeignKey(verbose_name='Vendue \xe0', blank=True, + to='bda.Participant', null=True), ), ] From bc4b06fc9246446a8de367eaee794e3cfad84327 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 26 Sep 2016 20:41:59 +0200 Subject: [PATCH 40/42] fix save --- bda/models.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bda/models.py b/bda/models.py index b97db2c2..61937723 100644 --- a/bda/models.py +++ b/bda/models.py @@ -280,6 +280,7 @@ class SpectacleRevente(models.Model): connection = mail.get_connection() connection.send_messages(mails_to_send) self.notif_sent = True + self.save() def mail_shotgun(self): inscrits = self.attribution.spectacle.revente.select_related('user') @@ -300,6 +301,7 @@ class SpectacleRevente(models.Model): connection = mail.get_connection() connection.send_messages(mails_to_send) self.notif_sent = True + self.save() def tirage(self): inscrits = self.interested @@ -331,3 +333,4 @@ Le BdA""" % (spectacle.title, winner.user.get_full_name(), winner.user.email) mail_seller, "bda@ens.fr", [seller.email], fail_silently=False) self.tirage_done = True + self.save() From 4a8d17f3543a75ccfe1eeb5e2d64b9d98105caa5 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 26 Sep 2016 20:44:22 +0200 Subject: [PATCH 41/42] change property --- bda/models.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bda/models.py b/bda/models.py index 61937723..c3b28d5b 100644 --- a/bda/models.py +++ b/bda/models.py @@ -234,7 +234,8 @@ class SpectacleRevente(models.Model): tirage_done = models.BooleanField("Tirage effectué", default=False) - def get_expiration_time(self): + @property + def expiration_time(self): # L'acheteur doit être connu au plus 12h avant le spectacle remaining_time = (self.attribution.spectacle.date - self.date - timedelta(hours=13)) @@ -242,9 +243,9 @@ class SpectacleRevente(models.Model): delay = min(remaining_time, timedelta(days=2)) # On a aussi 1h pour changer d'avis return self.date + delay + timedelta(hours=1) - expiration_time = property(get_expiration_time) - def get_shotgun(self): + @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 @@ -252,7 +253,6 @@ class SpectacleRevente(models.Model): (self.attribution.spectacle.date <= timezone.now() + timedelta(days=1))) and (timezone.now() >= self.date + timedelta(minutes=15)) - shotgun = property(get_shotgun) def __str__(self): return "%s -- %s" % (self.seller, From 77511d8acdd21c4cb4d20f6fd32d4a27a15a3568 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Tue, 27 Sep 2016 15:44:27 +0200 Subject: [PATCH 42/42] more coherent names --- bda/models.py | 14 +++++++------- bda/views.py | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/bda/models.py b/bda/models.py index c3b28d5b..1074e58b 100644 --- a/bda/models.py +++ b/bda/models.py @@ -161,7 +161,7 @@ class Participant(models.Model): blank=True) tirage = models.ForeignKey(Tirage) choicesrevente = models.ManyToManyField(Spectacle, - related_name="revente", + related_name="subscribed", blank=True) def __str__(self): @@ -220,9 +220,9 @@ class SpectacleRevente(models.Model): related_name="revente") date = models.DateTimeField("Date de mise en vente", default=timezone.now) - interested = models.ManyToManyField(Participant, - related_name="wanted", - blank=True) + answered_mail = models.ManyToManyField(Participant, + related_name="wanted", + blank=True) seller = models.ForeignKey(Participant, related_name="original_shows", verbose_name="Vendeur") @@ -262,7 +262,7 @@ class SpectacleRevente(models.Model): verbose_name = "Revente" def send_notif(self): - inscrits = self.attribution.spectacle.revente.select_related('user') + inscrits = self.attribution.spectacle.subscribed.select_related('user') mails_to_send = [] mail_object = "%s" % (self.attribution.spectacle) @@ -283,7 +283,7 @@ class SpectacleRevente(models.Model): self.save() def mail_shotgun(self): - inscrits = self.attribution.spectacle.revente.select_related('user') + inscrits = self.attribution.spectacle.subscribed.select_related('user') mails_to_send = [] mail_object = "%s" % (self.attribution.spectacle) @@ -304,7 +304,7 @@ class SpectacleRevente(models.Model): self.save() def tirage(self): - inscrits = self.interested + inscrits = self.answered_mail spectacle = self.attribution.spectacle seller = self.seller if inscrits.exists(): diff --git a/bda/views.py b/bda/views.py index 79212710..e9a000cb 100644 --- a/bda/views.py +++ b/bda/views.py @@ -333,7 +333,7 @@ def revente(request, tirage_id): revente = rev.get() revente.date = timezone.now() - timedelta(hours=1) revente.soldTo = None - revente.interested = None + revente.answered_mail = None else: resellform = ResellForm(participant, prefix='resell') @@ -367,7 +367,7 @@ def revente_interested(request, revente_id): if timezone.now() < revente.date + timedelta(hours=1) or revente.shotgun: return render(request, "bda-wrongtime.html", {}) - revente.interested.add(participant) + revente.answered_mail.add(participant) return render(request, "bda-interested.html", {"spectacle": revente.attribution.spectacle, "date": revente.expiration_time}) @@ -409,7 +409,7 @@ def list_revente(request, tirage_id): if revente.shotgun and not revente.soldTo: deja_revente = True else: - revente.interested.add(participant) + revente.answered_mail.add(participant) break else: form = InscriptionReventeForm(