forked from DGNum/gestioCOF
Merge branch 'Kerl/fusion_bda' into 'master'
Kerl/fusion bda Ce patch fusionne les trois applications `bda`, `bda2`, `bda3` existantes en une seule qui peut gérer autant de tirage que souhaité par le BdA. Après avoir appliqué ce patch, il est nécessaire d'effectuer les migrations qui vont avec : `python manage.py migrate` Fixes #3 See merge request !17
This commit is contained in:
commit
fb16276ee3
56 changed files with 339 additions and 3277 deletions
91
bda/admin.py
91
bda/admin.py
|
@ -5,7 +5,9 @@ from django.contrib.contenttypes.models import ContentType
|
|||
|
||||
from django.contrib import admin
|
||||
from django.db.models import Sum, Count
|
||||
from bda.models import Spectacle, Salle, Participant, ChoixSpectacle, Attribution
|
||||
from bda.models import Spectacle, Salle, Participant, ChoixSpectacle, Attribution, Tirage
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
class ChoixSpectacleInline(admin.TabularInline):
|
||||
model = ChoixSpectacle
|
||||
|
@ -37,62 +39,38 @@ class ParticipantAdmin(admin.ModelAdmin):
|
|||
actions_on_bottom = True
|
||||
list_per_page = 400
|
||||
|
||||
def send_choices(self, request, queryset):
|
||||
for member in queryset.all():
|
||||
choices = member.choixspectacle_set.order_by('priority').all()
|
||||
if len(choices) == 0:
|
||||
continue
|
||||
mail = u"""Cher(e) %s,
|
||||
Voici tes choix de spectacles tels que notre système les a enregistrés :\n\n""" % member.user.get_full_name()
|
||||
next_rank = 1
|
||||
member_shows = {}
|
||||
for choice in choices:
|
||||
if choice.spectacle in member_shows: continue
|
||||
else: member_shows[choice.spectacle] = True
|
||||
extra = ""
|
||||
if choice.double:
|
||||
extra += u" ; deux places"
|
||||
if choice.autoquit:
|
||||
extra += u" ; désistement automatique"
|
||||
mail += u"- Choix %d : %s%s\n" % (next_rank, choice.spectacle, extra)
|
||||
next_rank += 1
|
||||
mail += u"""\nSi cette liste est incorrecte, merci de nous contacter au plus vite (avant samedi 6 octobre 18h).
|
||||
|
||||
Artistiquement,
|
||||
Le BdA"""
|
||||
send_mail ("Choix de spectacles (BdA du COF)", mail,
|
||||
"bda@ens.fr", [member.user.email],
|
||||
fail_silently = True)
|
||||
count = len(queryset.all())
|
||||
if count == 1:
|
||||
message_bit = u"1 membre a"
|
||||
plural = ""
|
||||
else:
|
||||
message_bit = u"%d membres ont" % count
|
||||
plural = "s"
|
||||
self.message_user(request, u"%s été informé%s avec succès." % (message_bit, plural))
|
||||
send_choices.short_description = u"Envoyer les choix par mail"
|
||||
|
||||
def send_attribs(self, request, queryset):
|
||||
for member in queryset.all():
|
||||
attribs = member.attributions.all()
|
||||
if len(attribs) == 0:
|
||||
continue
|
||||
mail = u"""Cher(e) %s,
|
||||
mail = u"""Cher-e %s,
|
||||
|
||||
Tu t'es inscrit(e) pour le tirage au sort du BdA. Tu as été sélectionné(e)
|
||||
Tu t'es inscrit-e pour le tirage au sort du BdA. Malheureusement, tu n'as
|
||||
obtenu aucune place.
|
||||
|
||||
Nous proposons cependant de nombreuses offres hors-tirage tout au long de
|
||||
l'année, et nous t'invitons à nous contacter si l'une d'entre elles t'intéresse !
|
||||
--
|
||||
Le Bureau des Arts
|
||||
|
||||
"""
|
||||
name = member.user.get_full_name()
|
||||
mail = mail % name
|
||||
else:
|
||||
mail = u"""Cher-e %s,
|
||||
|
||||
Tu t'es inscrit-e pour le tirage au sort du BdA. Tu as été sélectionné-e
|
||||
pour les spectacles suivants :
|
||||
|
||||
%s
|
||||
|
||||
*Paiement*
|
||||
L'intégralité de ces places de spectacles est à régler à partir du lundi
|
||||
6 octobre et AVANT le vendredi 10 octobre, au bureau du COF pendant les
|
||||
heures de permanences (du lundi au vendredi entre 12h et 14h, et entre 18h
|
||||
et 20h). Des facilités de paiement sont bien évidemment possibles : nous
|
||||
pouvons ne pas encaisser le chèque immédiatement, ou bien découper votre
|
||||
paiement en deux fois. Pour ceux qui ne pourraient pas venir payer au bureau,
|
||||
merci de nous contacter par mail.
|
||||
L'intégralité de ces places de spectacles est à régler dès maintenant et AVANT
|
||||
le %s, au bureau du COF pendant les heures de permanences (du lundi au vendredi
|
||||
entre 12h et 14h, et entre 18h et 20h). Des facilités de paiement sont bien
|
||||
évidemment possibles : nous pouvons ne pas encaisser le chèque immédiatement, ou
|
||||
bien découper votre paiement en deux fois. Pour ceux qui ne pourraient pas venir
|
||||
payer au bureau, merci de nous contacter par mail.
|
||||
|
||||
*Mode de retrait des places*
|
||||
Au moment du paiement, certaines places vous seront remises directement, d'autres
|
||||
|
@ -109,16 +87,16 @@ prochainement disponible, directement sur votre compte GestioCOF.
|
|||
En vous souhaitant de très beaux spectacles tout au long de l'année,
|
||||
--
|
||||
Le Bureau des Arts
|
||||
(Jean, Antoine, Élodie, Marion et Louise)
|
||||
"""
|
||||
attribs_text = ""
|
||||
name = member.user.get_full_name()
|
||||
for attrib in attribs:
|
||||
attribs_text += u"- 1 place pour %s\n" % attrib
|
||||
mail = mail % (name, attribs_text)
|
||||
send_mail ("Résultats du tirage au sort", mail,
|
||||
"bda@ens.fr", [member.user.email],
|
||||
fail_silently = True)
|
||||
attribs_text = ""
|
||||
name = member.user.get_full_name()
|
||||
for attrib in attribs:
|
||||
attribs_text += u"- 1 place pour %s\n" % attrib
|
||||
deadline = member.tirage.fermeture + timedelta(days=7)
|
||||
mail = mail % (name, attribs_text, deadline.strftime('%d %b %Y'))
|
||||
send_mail ("Résultats du tirage au sort", mail,
|
||||
"bda@ens.fr", [member.user.email],
|
||||
fail_silently = True)
|
||||
count = len(queryset.all())
|
||||
if count == 1:
|
||||
message_bit = u"1 membre a"
|
||||
|
@ -155,3 +133,4 @@ admin.site.register(Salle)
|
|||
admin.site.register(Participant, ParticipantAdmin)
|
||||
admin.site.register(Attribution, AttributionAdmin)
|
||||
admin.site.register(ChoixSpectacle, ChoixSpectacleAdmin)
|
||||
admin.site.register(Tirage)
|
||||
|
|
|
@ -59,12 +59,6 @@ class Algorithm(object):
|
|||
self.origranks[member][show],
|
||||
self.choices[member][show].double))
|
||||
|
||||
"""
|
||||
Pour les 2 Walkyries: c'est Sandefer
|
||||
|
||||
Pour les 4 places pour l'Oratorio c'est Maxence Arutkin
|
||||
"""
|
||||
|
||||
def __call__(self, seed):
|
||||
random.seed(seed)
|
||||
results = []
|
||||
|
@ -102,3 +96,4 @@ class Algorithm(object):
|
|||
self.IncrementRanks(member, i)
|
||||
results.append((show,winners,losers))
|
||||
return results
|
||||
|
||||
|
|
12
bda/forms.py
12
bda/forms.py
|
@ -1,7 +1,7 @@
|
|||
# coding: utf-8
|
||||
|
||||
from django import forms
|
||||
from django.forms.models import inlineformset_factory, BaseInlineFormSet
|
||||
from django.forms.models import BaseInlineFormSet
|
||||
from bda.models import Spectacle, Participant, ChoixSpectacle, Attribution
|
||||
|
||||
class BaseBdaFormSet(BaseInlineFormSet):
|
||||
|
@ -19,19 +19,21 @@ class BaseBdaFormSet(BaseInlineFormSet):
|
|||
spectacle = form.cleaned_data['spectacle']
|
||||
delete = form.cleaned_data['DELETE']
|
||||
if not delete and spectacle in spectacles:
|
||||
raise forms.ValidationError("Vous ne pouvez pas vous inscrire deux fois pour le même spectacle.")
|
||||
raise forms.ValidationError("Vous ne pouvez pas vous " + \
|
||||
"inscrire deux fois pour le même spectacle.")
|
||||
spectacles.append(spectacle)
|
||||
|
||||
class TokenForm(forms.Form):
|
||||
token = forms.CharField(widget = forms.widgets.Textarea())
|
||||
token = forms.CharField(widget=forms.widgets.Textarea())
|
||||
|
||||
class SpectacleModelChoiceField(forms.ModelChoiceField):
|
||||
def label_from_instance(self, obj):
|
||||
return u"%s le %s (%s) à %.02f€" % (obj.title, obj.date_no_seconds(), obj.location, obj.price)
|
||||
return u"%s le %s (%s) à %.02f€" % (obj.title, obj.date_no_seconds(),
|
||||
obj.location, obj.price)
|
||||
|
||||
class ResellForm(forms.Form):
|
||||
count = forms.ChoiceField(choices = (("1","1"),("2","2"),))
|
||||
spectacle = SpectacleModelChoiceField(queryset = Spectacle.objects.none())
|
||||
spectacle = SpectacleModelChoiceField(queryset=Spectacle.objects.none())
|
||||
|
||||
def __init__(self, participant, *args, **kwargs):
|
||||
super(ResellForm, self).__init__(*args, **kwargs)
|
||||
|
|
56
bda/migrations/0002_add_tirage.py
Normal file
56
bda/migrations/0002_add_tirage.py
Normal file
|
@ -0,0 +1,56 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
|
||||
def forwards_func(apps, schema_editor):
|
||||
Tirage = apps.get_model("bda", "Tirage")
|
||||
db_alias = schema_editor.connection.alias
|
||||
Tirage.objects.using(db_alias).bulk_create([
|
||||
Tirage(
|
||||
id=1,
|
||||
title="Tirage de test (migration)",
|
||||
active=False,
|
||||
ouverture=timezone.now(),
|
||||
fermeture=timezone.now()),
|
||||
])
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('bda', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Tirage',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('title', models.CharField(max_length=300, verbose_name=b'Titre')),
|
||||
('ouverture', models.DateTimeField(verbose_name=b"Date et heure d'ouverture du tirage")),
|
||||
('fermeture', models.DateTimeField(verbose_name=b'Date et heure de fermerture du tirage')),
|
||||
('token', models.TextField(verbose_name=b'Graine du tirage', blank=True)),
|
||||
('active', models.BooleanField(default=True, verbose_name=b'Tirage actif')),
|
||||
],
|
||||
),
|
||||
migrations.RunPython(forwards_func, migrations.RunPython.noop),
|
||||
migrations.AlterField(
|
||||
model_name='participant',
|
||||
name='user',
|
||||
field=models.ForeignKey(to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='participant',
|
||||
name='tirage',
|
||||
field=models.ForeignKey(default=1, to='bda.Tirage'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='spectacle',
|
||||
name='tirage',
|
||||
field=models.ForeignKey(default=1, to='bda.Tirage'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
|
@ -7,22 +7,36 @@ from django.contrib.auth.models import User
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.db.models.signals import post_save
|
||||
|
||||
class Salle (models.Model):
|
||||
name = models.CharField ("Nom", max_length = 300)
|
||||
address = models.TextField ("Adresse")
|
||||
class Tirage(models.Model):
|
||||
title = models.CharField("Titre", max_length=300)
|
||||
ouverture = models.DateTimeField("Date et heure d'ouverture du tirage")
|
||||
fermeture = models.DateTimeField("Date et heure de fermerture du tirage")
|
||||
token = models.TextField("Graine du tirage", blank=True)
|
||||
active = models.BooleanField("Tirage actif", default=False)
|
||||
|
||||
def date_no_seconds(self):
|
||||
return self.fermeture.strftime('%d %b %Y %H:%M')
|
||||
|
||||
def __unicode__(self):
|
||||
return u"%s - %s" % (self.title, self.date_no_seconds())
|
||||
|
||||
class Salle(models.Model):
|
||||
name = models.CharField("Nom", max_length = 300)
|
||||
address = models.TextField("Adresse")
|
||||
|
||||
def __unicode__ (self):
|
||||
return self.name
|
||||
|
||||
class Spectacle (models.Model):
|
||||
title = models.CharField ("Titre", max_length = 300)
|
||||
date = models.DateTimeField ("Date & heure")
|
||||
class Spectacle(models.Model):
|
||||
title = models.CharField("Titre", max_length=300)
|
||||
date = models.DateTimeField("Date & heure")
|
||||
location = models.ForeignKey(Salle)
|
||||
description = models.TextField ("Description", blank = True)
|
||||
slots_description = models.TextField ("Description des places", blank = True)
|
||||
price = models.FloatField("Prix d'une place", blank = True)
|
||||
slots = models.IntegerField ("Places")
|
||||
priority = models.IntegerField ("Priorité", default = 1000)
|
||||
description = models.TextField("Description", blank=True)
|
||||
slots_description = models.TextField("Description des places", blank=True)
|
||||
price = models.FloatField("Prix d'une place")
|
||||
slots = models.IntegerField("Places")
|
||||
priority = models.IntegerField("Priorité", default=1000)
|
||||
tirage = models.ForeignKey(Tirage)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Spectacle"
|
||||
|
@ -38,7 +52,8 @@ class Spectacle (models.Model):
|
|||
return self.date.strftime('%d %b %Y %H:%M')
|
||||
|
||||
def __unicode__ (self):
|
||||
return u"%s - %s, %s, %.02f€" % (self.title, self.date_no_seconds(), self.location, self.price)
|
||||
return u"%s - %s, %s, %.02f€" % (self.title, self.date_no_seconds(),
|
||||
self.location, self.price)
|
||||
|
||||
PAYMENT_TYPES = (
|
||||
("cash",u"Cash"),
|
||||
|
@ -47,12 +62,18 @@ PAYMENT_TYPES = (
|
|||
("autre",u"Autre"),
|
||||
)
|
||||
|
||||
class Participant (models.Model):
|
||||
user = models.OneToOneField(User)
|
||||
choices = models.ManyToManyField(Spectacle, through = "ChoixSpectacle", related_name = "chosen_by")
|
||||
attributions = models.ManyToManyField(Spectacle, through = "Attribution", related_name = "attributed_to")
|
||||
paid = models.BooleanField (u"A payé", default = False)
|
||||
paymenttype = models.CharField(u"Moyen de paiement", max_length = 6, choices = PAYMENT_TYPES, blank = True)
|
||||
class Participant(models.Model):
|
||||
user = models.ForeignKey(User)
|
||||
choices = models.ManyToManyField(Spectacle,
|
||||
through="ChoixSpectacle",
|
||||
related_name="chosen_by")
|
||||
attributions = models.ManyToManyField(Spectacle,
|
||||
through="Attribution",
|
||||
related_name="attributed_to")
|
||||
paid = models.BooleanField (u"A payé", default=False)
|
||||
paymenttype = models.CharField(u"Moyen de paiement",
|
||||
max_length=6, choices=PAYMENT_TYPES, blank=True)
|
||||
tirage = models.ForeignKey(Tirage)
|
||||
|
||||
def __unicode__ (self):
|
||||
return u"%s" % (self.user)
|
||||
|
@ -63,11 +84,12 @@ DOUBLE_CHOICES = (
|
|||
("double", "2 places sinon rien"),
|
||||
)
|
||||
|
||||
class ChoixSpectacle (models.Model):
|
||||
class ChoixSpectacle(models.Model):
|
||||
participant = models.ForeignKey(Participant)
|
||||
spectacle = models.ForeignKey(Spectacle, related_name = "participants")
|
||||
spectacle = models.ForeignKey(Spectacle, related_name="participants")
|
||||
priority = models.PositiveIntegerField("Priorité")
|
||||
double_choice = models.CharField("Nombre de places", default = "1", choices = DOUBLE_CHOICES, max_length = 10)
|
||||
double_choice = models.CharField("Nombre de places",
|
||||
default="1", choices=DOUBLE_CHOICES, max_length=10)
|
||||
|
||||
def get_double(self):
|
||||
return self.double_choice != "1"
|
||||
|
@ -83,10 +105,11 @@ class ChoixSpectacle (models.Model):
|
|||
verbose_name = "voeu"
|
||||
verbose_name_plural = "voeux"
|
||||
|
||||
class Attribution (models.Model):
|
||||
class Attribution(models.Model):
|
||||
participant = models.ForeignKey(Participant)
|
||||
spectacle = models.ForeignKey(Spectacle, related_name = "attribues")
|
||||
given = models.BooleanField(u"Donnée", default = False)
|
||||
spectacle = models.ForeignKey(Spectacle, related_name="attribues")
|
||||
given = models.BooleanField(u"Donnée", default=False)
|
||||
|
||||
def __unicode__ (self):
|
||||
return u"%s -- %s" % (self.participant, self.spectacle)
|
||||
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
{% block extracontent %}
|
||||
|
||||
<h1>Attribution (détails)</h1>
|
||||
<h2>Token : {{ token }}</h2>
|
||||
<h2>Token :</h2>
|
||||
<pre>{{ token }}</pre>
|
||||
<h2>Placés : {{ total_slots }} ; Déçus : {{ total_losers }}</h2>
|
||||
|
||||
<table>
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
{% block realcontent %}
|
||||
|
||||
<h1>Attribution</h1>
|
||||
<h2>Token : {{ token }}</h2>
|
||||
<h2>Token :</h2>
|
||||
<pre>{{ token }}</pre>
|
||||
<h2>Placés : {{ total_slots }} ; Déçus : {{ total_losers }}</h2>
|
||||
{% if user.profile.is_buro %}<h2>Déficit total: {{ total_deficit }} €, Opéra: {{ opera_deficit }} €, Attribué: {{ total_sold }} €</h2>{% endif %}
|
||||
<h2>Temps de calcul : {{ duration|floatformat }}s</h2>
|
||||
|
|
|
@ -98,7 +98,7 @@ var django = {
|
|||
{% if stateerror %}
|
||||
<p class="error">Impossible d'enregistrer vos modifications: vous avez apporté d'autres modifications entre temps</p>
|
||||
{% endif %}
|
||||
<form id="bda_form" method="post" action="{% url 'bda-tirage-inscription' %}">
|
||||
<form id="bda_form" method="post" action="{% url 'bda-tirage-inscription' tirage.id %}">
|
||||
{% csrf_token %}
|
||||
{% include "inscription-formset.html" %}
|
||||
<input type="button" class="btn-addmore" value="Ajouter un autre vœu" id="add_more">
|
||||
|
|
|
@ -1,116 +0,0 @@
|
|||
{% extends "base_title.html" %}
|
||||
|
||||
{% block extra_head %}
|
||||
<link href="{{ STATIC_URL }}grappelli/jquery/ui/css/custom-theme/jquery-ui-1.8.custom.css" rel="stylesheet" type="text/css" media="screen" title="no title" charset="utf-8" />
|
||||
<script src="{{ STATIC_URL }}grappelli/jquery/jquery-1.6.2.min.js" type="text/javascript"></script>
|
||||
<script src="{{ STATIC_URL }}grappelli/jquery/ui/js/jquery-ui-1.8.15.custom.min.js" type="text/javascript"></script>
|
||||
<link href="{{ STATIC_URL }}grappelli/css/tools.css" rel="stylesheet" type="text/css" />
|
||||
<link href="{{ STATIC_URL }}grappelli/css/jquery-ui-grappelli-extensions.css" rel="stylesheet" type="text/css" />
|
||||
{% endblock %}
|
||||
|
||||
{% block realcontent %}
|
||||
<script type="text/javascript">
|
||||
var django = {
|
||||
"jQuery": jQuery.noConflict(true)
|
||||
};
|
||||
|
||||
(function($) {
|
||||
cloneMore = function(selector, type) {
|
||||
var newElement = $(selector).clone(true);
|
||||
var total = $('#id_' + type + '-TOTAL_FORMS').val();
|
||||
newElement.find(':input').each(function() {
|
||||
var name = $(this).attr('name').replace('-' + (total-1) + '-','-' + total + '-');
|
||||
var id = 'id_' + name;
|
||||
$(this).attr({'name': name, 'id': id}).val('').removeAttr('checked');
|
||||
});
|
||||
newElement.find('label').each(function() {
|
||||
var newFor = $(this).attr('for').replace('-' + (total-1) + '-','-' + total + '-');
|
||||
$(this).attr('for', newFor);
|
||||
});
|
||||
total++;
|
||||
$('#id_' + type + '-TOTAL_FORMS').val(total);
|
||||
$(selector).after(newElement);
|
||||
}
|
||||
deleteButtonHandler = function(elem) {
|
||||
elem.bind("click", function() {
|
||||
var deleteInput = $(this).prev().prev(),
|
||||
form = $(this).parents(".dynamic-form").first();
|
||||
// callback
|
||||
// toggle options.predeleteCssClass and toggle checkbox
|
||||
if (form.hasClass("has_original")) {
|
||||
form.toggleClass("predelete");
|
||||
if (deleteInput.attr("checked")) {
|
||||
deleteInput.attr("checked", false);
|
||||
} else {
|
||||
deleteInput.attr("checked", true);
|
||||
}
|
||||
}
|
||||
// callback
|
||||
});
|
||||
};
|
||||
$(document).ready(function($) {
|
||||
deleteButtonHandler($("table#bda_formset tbody.bda_formset_content").find("a.delete-handler"));
|
||||
$("table#bda_formset tbody.bda_formset_content").sortable({
|
||||
handle: "a.drag-handler",
|
||||
items: "tr",
|
||||
axis: "y",
|
||||
appendTo: 'body',
|
||||
forceHelperSize: true,
|
||||
placeholder: 'ui-sortable-placeholder',
|
||||
forcePlaceholderSize: true,
|
||||
containment: 'form#bda_form',
|
||||
tolerance: 'pointer',
|
||||
start: function(evt, ui) {
|
||||
var template = "",
|
||||
len = ui.item.children("td").length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
template += "<td style='height:" + (ui.item.outerHeight() + 12 ) + "px' class='placeholder-cell'> </td>"
|
||||
}
|
||||
template += "";
|
||||
ui.placeholder.html(template);
|
||||
},
|
||||
stop: function(evt, ui) {
|
||||
// Toggle div.table twice to remove webkits border-spacing bug
|
||||
$("table#bda_formset").toggle().toggle();
|
||||
},
|
||||
});
|
||||
$("#bda_form").bind("submit", function(){
|
||||
var sortable_field_name = "priority";
|
||||
var i = 1;
|
||||
$(".bda_formset_content").find("tr").each(function(){
|
||||
var fields = $(this).find("td :input[value]"),
|
||||
select = $(this).find("td select");
|
||||
if (select.val() && fields.serialize()) {
|
||||
$(this).find("input[name$='"+sortable_field_name+"']").val(i);
|
||||
i++;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
})(django.jQuery);
|
||||
</script>
|
||||
|
||||
<h2>Inscription au tirage au sort du BDA</h2>
|
||||
{% if success %}
|
||||
<p class="success">Votre inscription a été mise à jour avec succès !</p>
|
||||
{% endif %}
|
||||
<form id="bda_form" method="post" action="{% url 'bda-tirage-inscription' %}">
|
||||
{% csrf_token %}
|
||||
{% include "inscription-formset.html" %}
|
||||
<input type="button" class="btn-addmore" value="Ajouter un autre vœu" id="add_more">
|
||||
<script>
|
||||
django.jQuery('#add_more').click(function() {
|
||||
cloneMore('tbody.bda_formset_content tr:last-child', 'choixspectacle_set');
|
||||
});
|
||||
</script>
|
||||
<input type="submit" class="btn-submit" value="Enregistrer" />
|
||||
Prix total actuel : {{ total_price }}€
|
||||
<hr />
|
||||
<p class="footnotes">
|
||||
<sup>1</sup>: demander deux places pour ce spectacle<br />
|
||||
<sup>2</sup>: abandonner une place si impossible d'en obtenir une seconde pour ce spectacle (si vous avez coché l'option <tt>Deux places</tt> pour ce spectacle)<br />
|
||||
<sup>3</sup>: cette liste de vœu est ordonnée (du plus important au moins important), pour ajuster la priorité vous pouvez déplacer chaque vœu<br />
|
||||
</p>
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -1,120 +0,0 @@
|
|||
{% extends "base_title.html" %}
|
||||
|
||||
{% block extra_head %}
|
||||
<link href="{{ STATIC_URL }}grappelli/jquery/ui/css/custom-theme/jquery-ui-1.8.custom.css" rel="stylesheet" type="text/css" media="screen" title="no title" charset="utf-8" />
|
||||
<script src="{{ STATIC_URL }}grappelli/jquery/jquery-1.6.2.min.js" type="text/javascript"></script>
|
||||
<script src="{{ STATIC_URL }}grappelli/jquery/ui/js/jquery-ui-1.8.15.custom.min.js" type="text/javascript"></script>
|
||||
<link href="{{ STATIC_URL }}grappelli/css/tools.css" rel="stylesheet" type="text/css" />
|
||||
<link href="{{ STATIC_URL }}grappelli/css/jquery-ui-grappelli-extensions.css" rel="stylesheet" type="text/css" />
|
||||
{% endblock %}
|
||||
|
||||
{% block realcontent %}
|
||||
<script type="text/javascript">
|
||||
var django = {
|
||||
"jQuery": jQuery.noConflict(true)
|
||||
};
|
||||
|
||||
(function($) {
|
||||
cloneMore = function(selector, type) {
|
||||
var newElement = $(selector).clone(true);
|
||||
var total = $('#id_' + type + '-TOTAL_FORMS').val();
|
||||
newElement.find(':input').each(function() {
|
||||
var name = $(this).attr('name').replace('-' + (total-1) + '-','-' + total + '-');
|
||||
var id = 'id_' + name;
|
||||
$(this).attr({'name': name, 'id': id}).val('').removeAttr('checked');
|
||||
});
|
||||
newElement.find('label').each(function() {
|
||||
var newFor = $(this).attr('for').replace('-' + (total-1) + '-','-' + total + '-');
|
||||
$(this).attr('for', newFor);
|
||||
});
|
||||
total++;
|
||||
$('#id_' + type + '-TOTAL_FORMS').val(total);
|
||||
$(selector).after(newElement);
|
||||
}
|
||||
deleteButtonHandler = function(elem) {
|
||||
elem.bind("click", function() {
|
||||
var deleteInput = $(this).prev().prev(),
|
||||
form = $(this).parents(".dynamic-form").first();
|
||||
// callback
|
||||
// toggle options.predeleteCssClass and toggle checkbox
|
||||
if (form.hasClass("has_original")) {
|
||||
form.toggleClass("predelete");
|
||||
if (deleteInput.attr("checked")) {
|
||||
deleteInput.attr("checked", false);
|
||||
} else {
|
||||
deleteInput.attr("checked", true);
|
||||
}
|
||||
}
|
||||
// callback
|
||||
});
|
||||
};
|
||||
$(document).ready(function($) {
|
||||
deleteButtonHandler($("table#bda_formset tbody.bda_formset_content").find("a.delete-handler"));
|
||||
$("table#bda_formset tbody.bda_formset_content").sortable({
|
||||
handle: "a.drag-handler",
|
||||
items: "tr",
|
||||
axis: "y",
|
||||
appendTo: 'body',
|
||||
forceHelperSize: true,
|
||||
placeholder: 'ui-sortable-placeholder',
|
||||
forcePlaceholderSize: true,
|
||||
containment: 'form#bda_form',
|
||||
tolerance: 'pointer',
|
||||
start: function(evt, ui) {
|
||||
var template = "",
|
||||
len = ui.item.children("td").length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
template += "<td style='height:" + (ui.item.outerHeight() + 12 ) + "px' class='placeholder-cell'> </td>"
|
||||
}
|
||||
template += "";
|
||||
ui.placeholder.html(template);
|
||||
},
|
||||
stop: function(evt, ui) {
|
||||
// Toggle div.table twice to remove webkits border-spacing bug
|
||||
$("table#bda_formset").toggle().toggle();
|
||||
},
|
||||
});
|
||||
$("#bda_form").bind("submit", function(){
|
||||
var sortable_field_name = "priority";
|
||||
var i = 1;
|
||||
$(".bda_formset_content").find("tr").each(function(){
|
||||
var fields = $(this).find("td :input[value]"),
|
||||
select = $(this).find("td select");
|
||||
if (select.val() && fields.serialize()) {
|
||||
$(this).find("input[name$='"+sortable_field_name+"']").val(i);
|
||||
i++;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
})(django.jQuery);
|
||||
</script>
|
||||
|
||||
<h2>Inscription au tirage au sort du BdA</h2>
|
||||
{% if success %}
|
||||
<p class="success">Votre inscription a été mise à jour avec succès !</p>
|
||||
{% endif %}
|
||||
{% if stateerror %}
|
||||
<p class="error">Impossible d'enregistrer vos modifications: vous avez apporté d'autres modifications entre temps</p>
|
||||
{% endif %}
|
||||
<form id="bda_form" method="post" action="{% url 'bda2-tirage-inscription' %}">
|
||||
{% csrf_token %}
|
||||
{% include "inscription-formset.html" %}
|
||||
<input type="button" class="btn-addmore" value="Ajouter un autre vœu" id="add_more">
|
||||
<script>
|
||||
django.jQuery('#add_more').click(function() {
|
||||
cloneMore('tbody.bda_formset_content tr:last-child', 'choixspectacle_set');
|
||||
});
|
||||
</script>
|
||||
<input type="hidden" name="dbstate" value="{{ dbstate }}" />
|
||||
<input type="submit" class="btn-submit" value="Enregistrer" />
|
||||
Prix total actuel : {{ total_price }}€
|
||||
<hr />
|
||||
<p class="footnotes">
|
||||
<sup>1</sup>: demander deux places pour ce spectable<br />
|
||||
<sup>2</sup>: abandonner une place si impossible d'en obtenir une seconde pour ce spectacle (si vous avez coché l'option <tt>Deux places</tt> pour ce spectacle)<br />
|
||||
<sup>3</sup>: cette liste de vœu est ordonnée (du plus important au moins important), pour ajuster la priorité vous pouvez déplacer chaque vœu<br />
|
||||
</p>
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -1,120 +0,0 @@
|
|||
{% extends "base_title.html" %}
|
||||
|
||||
{% block extra_head %}
|
||||
<link href="{{ STATIC_URL }}grappelli/jquery/ui/css/custom-theme/jquery-ui-1.8.custom.css" rel="stylesheet" type="text/css" media="screen" title="no title" charset="utf-8" />
|
||||
<script src="{{ STATIC_URL }}grappelli/jquery/jquery-1.6.2.min.js" type="text/javascript"></script>
|
||||
<script src="{{ STATIC_URL }}grappelli/jquery/ui/js/jquery-ui-1.8.15.custom.min.js" type="text/javascript"></script>
|
||||
<link href="{{ STATIC_URL }}grappelli/css/tools.css" rel="stylesheet" type="text/css" />
|
||||
<link href="{{ STATIC_URL }}grappelli/css/jquery-ui-grappelli-extensions.css" rel="stylesheet" type="text/css" />
|
||||
{% endblock %}
|
||||
|
||||
{% block realcontent %}
|
||||
<script type="text/javascript">
|
||||
var django = {
|
||||
"jQuery": jQuery.noConflict(true)
|
||||
};
|
||||
|
||||
(function($) {
|
||||
cloneMore = function(selector, type) {
|
||||
var newElement = $(selector).clone(true);
|
||||
var total = $('#id_' + type + '-TOTAL_FORMS').val();
|
||||
newElement.find(':input').each(function() {
|
||||
var name = $(this).attr('name').replace('-' + (total-1) + '-','-' + total + '-');
|
||||
var id = 'id_' + name;
|
||||
$(this).attr({'name': name, 'id': id}).val('').removeAttr('checked');
|
||||
});
|
||||
newElement.find('label').each(function() {
|
||||
var newFor = $(this).attr('for').replace('-' + (total-1) + '-','-' + total + '-');
|
||||
$(this).attr('for', newFor);
|
||||
});
|
||||
total++;
|
||||
$('#id_' + type + '-TOTAL_FORMS').val(total);
|
||||
$(selector).after(newElement);
|
||||
}
|
||||
deleteButtonHandler = function(elem) {
|
||||
elem.bind("click", function() {
|
||||
var deleteInput = $(this).prev().prev(),
|
||||
form = $(this).parents(".dynamic-form").first();
|
||||
// callback
|
||||
// toggle options.predeleteCssClass and toggle checkbox
|
||||
if (form.hasClass("has_original")) {
|
||||
form.toggleClass("predelete");
|
||||
if (deleteInput.attr("checked")) {
|
||||
deleteInput.attr("checked", false);
|
||||
} else {
|
||||
deleteInput.attr("checked", true);
|
||||
}
|
||||
}
|
||||
// callback
|
||||
});
|
||||
};
|
||||
$(document).ready(function($) {
|
||||
deleteButtonHandler($("table#bda_formset tbody.bda_formset_content").find("a.delete-handler"));
|
||||
$("table#bda_formset tbody.bda_formset_content").sortable({
|
||||
handle: "a.drag-handler",
|
||||
items: "tr",
|
||||
axis: "y",
|
||||
appendTo: 'body',
|
||||
forceHelperSize: true,
|
||||
placeholder: 'ui-sortable-placeholder',
|
||||
forcePlaceholderSize: true,
|
||||
containment: 'form#bda_form',
|
||||
tolerance: 'pointer',
|
||||
start: function(evt, ui) {
|
||||
var template = "",
|
||||
len = ui.item.children("td").length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
template += "<td style='height:" + (ui.item.outerHeight() + 12 ) + "px' class='placeholder-cell'> </td>"
|
||||
}
|
||||
template += "";
|
||||
ui.placeholder.html(template);
|
||||
},
|
||||
stop: function(evt, ui) {
|
||||
// Toggle div.table twice to remove webkits border-spacing bug
|
||||
$("table#bda_formset").toggle().toggle();
|
||||
},
|
||||
});
|
||||
$("#bda_form").bind("submit", function(){
|
||||
var sortable_field_name = "priority";
|
||||
var i = 1;
|
||||
$(".bda_formset_content").find("tr").each(function(){
|
||||
var fields = $(this).find("td :input[value]"),
|
||||
select = $(this).find("td select");
|
||||
if (select.val() && fields.serialize()) {
|
||||
$(this).find("input[name$='"+sortable_field_name+"']").val(i);
|
||||
i++;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
})(django.jQuery);
|
||||
</script>
|
||||
|
||||
<h2>Inscription au tirage au sort du BdA</h2>
|
||||
{% if success %}
|
||||
<p class="success">Votre inscription a été mise à jour avec succès !</p>
|
||||
{% endif %}
|
||||
{% if stateerror %}
|
||||
<p class="error">Impossible d'enregistrer vos modifications: vous avez apporté d'autres modifications entre temps</p>
|
||||
{% endif %}
|
||||
<form id="bda_form" method="post" action="{% url 'bda3-tirage-inscription' %}">
|
||||
{% csrf_token %}
|
||||
{% include "inscription-formset.html" %}
|
||||
<input type="button" class="btn-addmore" value="Ajouter un autre vœu" id="add_more">
|
||||
<script>
|
||||
django.jQuery('#add_more').click(function() {
|
||||
cloneMore('tbody.bda_formset_content tr:last-child', 'choixspectacle_set');
|
||||
});
|
||||
</script>
|
||||
<input type="hidden" name="dbstate" value="{{ dbstate }}" />
|
||||
<input type="submit" class="btn-submit" value="Enregistrer" />
|
||||
Prix total actuel : {{ total_price }}€
|
||||
<hr />
|
||||
<p class="footnotes">
|
||||
<sup>1</sup>: demander deux places pour ce spectable<br />
|
||||
<sup>2</sup>: abandonner une place si impossible d'en obtenir une seconde pour ce spectacle (si vous avez coché l'option <tt>Deux places</tt> pour ce spectacle)<br />
|
||||
<sup>3</sup>: cette liste de vœu est ordonnée (du plus important au moins important), pour ajuster la priorité vous pouvez déplacer chaque vœu<br />
|
||||
</p>
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -12,7 +12,7 @@
|
|||
{% endfor %}
|
||||
</ol>
|
||||
<h4>Total à payer : {{ total|floatformat }}€</h4>
|
||||
<h4><a href="{% url "bda-places-attribuees-ics" %}">Exporter au format calendrier</a> (.ics, compatible avec tous les logiciels d'agenda)</h4>
|
||||
<h4><a href="{% url "bda-places-attribuees-ics" tirage.id %}">Exporter au format calendrier</a> (.ics, compatible avec tous les logiciels d'agenda)</h4>
|
||||
{% else %}
|
||||
<h3>Vous n'avez aucune place :(</h3>
|
||||
{% endif %}
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
{% block realcontent %}
|
||||
<h1><strong>Spectacles</strong></h1>
|
||||
<ul>
|
||||
<li><a href="{% url 'bda-unpaid' %}">Pas payé</a></li>
|
||||
<li><a href="{% url 'bda-unpaid' tirage_id %}">Pas payé</a></li>
|
||||
{% for spectacle in object_list %}
|
||||
<li><a href="{% url 'bda-spectacle' spectacle.id %}">{{ spectacle }}</a></li>
|
||||
<li><a href="{% url 'bda-spectacle' tirage_id spectacle.id %}">{{ spectacle }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
|
178
bda/views.py
178
bda/views.py
|
@ -6,25 +6,39 @@ from django.contrib.auth.decorators import login_required
|
|||
from django.db import models
|
||||
from django.http import Http404
|
||||
from django.core import serializers
|
||||
from django.forms.models import inlineformset_factory
|
||||
import hashlib
|
||||
|
||||
from django.core.mail import send_mail
|
||||
from django.utils import timezone
|
||||
from django.views.generic.list import ListView
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import timedelta
|
||||
import time
|
||||
|
||||
from gestioncof.decorators import cof_required, buro_required
|
||||
from gestioncof.shared import send_custom_mail
|
||||
from bda.models import Spectacle, Participant, ChoixSpectacle, Attribution
|
||||
from bda.models import Spectacle, Participant, ChoixSpectacle, Attribution, Tirage
|
||||
from bda.algorithm import Algorithm
|
||||
|
||||
from bda.forms import BaseBdaFormSet, TokenForm, ResellForm
|
||||
|
||||
@cof_required
|
||||
def etat_places(request):
|
||||
spectacles1 = ChoixSpectacle.objects.filter(double_choice = "1").all().values('spectacle','spectacle__title').annotate(total = models.Count('spectacle'))
|
||||
spectacles2 = ChoixSpectacle.objects.exclude(double_choice = "1").all().values('spectacle','spectacle__title').annotate(total = models.Count('spectacle'))
|
||||
spectacles = Spectacle.objects.all()
|
||||
def etat_places(request, tirage_id):
|
||||
tirage = get_object_or_404(Tirage, id=tirage_id)
|
||||
spectacles1 = ChoixSpectacle.objects \
|
||||
.filter(spectacle__tirage=tirage) \
|
||||
.filter(double_choice="1") \
|
||||
.all() \
|
||||
.values('spectacle','spectacle__title') \
|
||||
.annotate(total=models.Count('spectacle'))
|
||||
spectacles2 = ChoixSpectacle.objects \
|
||||
.filter(spectacle__tirage=tirage) \
|
||||
.exclude(double_choice="1") \
|
||||
.all() \
|
||||
.values('spectacle','spectacle__title') \
|
||||
.annotate(total=models.Count('spectacle'))
|
||||
spectacles = tirage.spectacle_set.all()
|
||||
spectacles_dict = {}
|
||||
total = 0
|
||||
for spectacle in spectacles:
|
||||
|
@ -33,13 +47,18 @@ def etat_places(request):
|
|||
spectacles_dict[spectacle.id] = spectacle
|
||||
for spectacle in spectacles1:
|
||||
spectacles_dict[spectacle["spectacle"]].total += spectacle["total"]
|
||||
spectacles_dict[spectacle["spectacle"]].ratio = spectacles_dict[spectacle["spectacle"]].total/float(spectacles_dict[spectacle["spectacle"]].slots)
|
||||
spectacles_dict[spectacle["spectacle"]].ratio = \
|
||||
spectacles_dict[spectacle["spectacle"]].total / \
|
||||
float(spectacles_dict[spectacle["spectacle"]].slots)
|
||||
total += spectacle["total"]
|
||||
for spectacle in spectacles2:
|
||||
spectacles_dict[spectacle["spectacle"]].total += 2*spectacle["total"]
|
||||
spectacles_dict[spectacle["spectacle"]].ratio = spectacles_dict[spectacle["spectacle"]].total/float(spectacles_dict[spectacle["spectacle"]].slots)
|
||||
spectacles_dict[spectacle["spectacle"]].ratio = \
|
||||
spectacles_dict[spectacle["spectacle"]].total / \
|
||||
float(spectacles_dict[spectacle["spectacle"]].slots)
|
||||
total += spectacle["total"]
|
||||
return render(request, "etat-places.html", {"spectacles": spectacles, "total": total})
|
||||
return render(request, "etat-places.html",
|
||||
{"spectacles": spectacles, "total": total, 'tirage': tirage})
|
||||
|
||||
def _hash_queryset(queryset):
|
||||
data = serializers.serialize("json", queryset)
|
||||
|
@ -48,9 +67,12 @@ def _hash_queryset(queryset):
|
|||
return hasher.hexdigest()
|
||||
|
||||
@cof_required
|
||||
def places(request):
|
||||
participant, created = Participant.objects.get_or_create(user = request.user)
|
||||
places = participant.attribution_set.order_by("spectacle__date", "spectacle").all()
|
||||
def places(request, tirage_id):
|
||||
tirage = get_object_or_404(Tirage, id=tirage_id)
|
||||
participant, created = Participant.objects.get_or_create(
|
||||
user=request.user, tirage=tirage)
|
||||
places = participant.attribution_set.order_by(
|
||||
"spectacle__date", "spectacle").all()
|
||||
total = sum([place.spectacle.price for place in places])
|
||||
filtered_places = []
|
||||
places_dict = {}
|
||||
|
@ -73,13 +95,17 @@ def places(request):
|
|||
return render(request, "resume_places.html",
|
||||
{"participant": participant,
|
||||
"places": filtered_places,
|
||||
"tirage": tirage,
|
||||
"total": total,
|
||||
"warning": warning})
|
||||
|
||||
@cof_required
|
||||
def places_ics(request):
|
||||
participant, created = Participant.objects.get_or_create(user = request.user)
|
||||
places = participant.attribution_set.order_by("spectacle__date", "spectacle").all()
|
||||
def places_ics(request, tirage_id):
|
||||
tirage = get_object_or_404(Tirage, id=tirage_id)
|
||||
participant, created = Participant.objects.get_or_create(
|
||||
user=request.user, tirage=tirage)
|
||||
places = participant.attribution_set.order_by(
|
||||
"spectacle__date", "spectacle").all()
|
||||
filtered_places = []
|
||||
places_dict = {}
|
||||
spectacles = []
|
||||
|
@ -98,45 +124,75 @@ def places_ics(request):
|
|||
"places": filtered_places}, content_type="text/calendar")
|
||||
|
||||
@cof_required
|
||||
def inscription(request):
|
||||
if datetime.now() > datetime(2015, 10, 4, 12, 00):
|
||||
participant, created = Participant.objects.get_or_create(user = request.user)
|
||||
def inscription(request, tirage_id):
|
||||
tirage = get_object_or_404(Tirage, id=tirage_id)
|
||||
if timezone.now() < tirage.ouverture:
|
||||
error_desc = "Ouverture le %s" % (
|
||||
tirage.ouverture.strftime('%d %b %Y à %H:%M'))
|
||||
return render(request, 'resume_inscription.html',
|
||||
{ "error_title": "Le tirage n'est pas encore ouvert !",
|
||||
"error_description": error_desc })
|
||||
if timezone.now() > tirage.fermeture:
|
||||
participant, created = Participant.objects.get_or_create(
|
||||
user=request.user, tirage=tirage)
|
||||
choices = participant.choixspectacle_set.order_by("priority").all()
|
||||
return render(request, "resume_inscription.html", {"error_title": "C'est fini !", "error_description": u"Tirage au sort dans la journée !", "choices": choices})
|
||||
BdaFormSet = inlineformset_factory(Participant, ChoixSpectacle, fields = ("spectacle","double_choice","priority",), formset = BaseBdaFormSet)
|
||||
participant, created = Participant.objects.get_or_create(user = request.user)
|
||||
return render(request, "resume_inscription.html",
|
||||
{ "error_title": "C'est fini !",
|
||||
"error_description": u"Tirage au sort dans la journée !",
|
||||
"choices": choices})
|
||||
def formfield_callback(f, **kwargs):
|
||||
if f.name == "spectacle":
|
||||
kwargs['queryset'] = tirage.spectacle_set
|
||||
return f.formfield(**kwargs)
|
||||
BdaFormSet = inlineformset_factory(
|
||||
Participant,
|
||||
ChoixSpectacle,
|
||||
fields=("spectacle","double_choice","priority"),
|
||||
formset=BaseBdaFormSet,
|
||||
formfield_callback=formfield_callback)
|
||||
participant, created = Participant.objects.get_or_create(
|
||||
user=request.user, tirage=tirage)
|
||||
success = False
|
||||
stateerror = False
|
||||
if request.method == "POST":
|
||||
dbstate = _hash_queryset(participant.choixspectacle_set.all())
|
||||
if "dbstate" in request.POST and dbstate != request.POST["dbstate"]:
|
||||
stateerror = True
|
||||
formset = BdaFormSet(instance = participant)
|
||||
formset = BdaFormSet(instance=participant)
|
||||
else:
|
||||
formset = BdaFormSet(request.POST, instance = participant)
|
||||
formset = BdaFormSet(request.POST, instance=participant)
|
||||
if formset.is_valid():
|
||||
#ChoixSpectacle.objects.filter(participant = participant).delete()
|
||||
formset.save()
|
||||
success = True
|
||||
formset = BdaFormSet(instance = participant)
|
||||
formset = BdaFormSet(instance=participant)
|
||||
else:
|
||||
formset = BdaFormSet(instance = participant)
|
||||
formset = BdaFormSet(instance=participant)
|
||||
dbstate = _hash_queryset(participant.choixspectacle_set.all())
|
||||
total_price = 0
|
||||
for choice in participant.choixspectacle_set.all():
|
||||
total_price += choice.spectacle.price
|
||||
if choice.double: total_price += choice.spectacle.price
|
||||
return render(request, "inscription-bda.html", {"formset": formset, "success": success, "total_price": total_price, "dbstate": dbstate, "stateerror": stateerror})
|
||||
return render(request, "inscription-bda.html",
|
||||
{ "formset": formset,
|
||||
"success": success,
|
||||
"total_price": total_price,
|
||||
"dbstate": dbstate,
|
||||
'tirage': tirage,
|
||||
"stateerror": stateerror})
|
||||
|
||||
def do_tirage(request):
|
||||
def do_tirage(request, tirage_id):
|
||||
tirage_elt = get_object_or_404(Tirage, id=tirage_id)
|
||||
form = TokenForm(request.POST)
|
||||
if not form.is_valid():
|
||||
return tirage(request)
|
||||
tirage_elt.token = form.cleaned_data['token']
|
||||
tirage_elt.save()
|
||||
start = time.time()
|
||||
data = {}
|
||||
shows = Spectacle.objects.select_related().all()
|
||||
members = Participant.objects.all()
|
||||
choices = ChoixSpectacle.objects.order_by('participant', 'priority').select_related().all()
|
||||
shows = tirage_elt.spectacle_set.select_related().all()
|
||||
members = tirage_elt.spectacle_set.all()
|
||||
choices = ChoixSpectacle.objects.filter(spectacle__tirage=tirage_elt).order_by(
|
||||
'participant', 'priority').select_related().all()
|
||||
algo = Algorithm(shows, members, choices)
|
||||
results = algo(form.cleaned_data["token"])
|
||||
total_slots = 0
|
||||
|
@ -178,23 +234,26 @@ def do_tirage(request):
|
|||
members2[member].append(show)
|
||||
member.total += show.price
|
||||
members2 = members2.items()
|
||||
data["members2"] = sorted(members2, key = lambda m: m[0].user.last_name)
|
||||
if False and request.user.username in ["seguin", "harazi","fromherz", "ccadiou"]:
|
||||
data["members2"] = sorted(members2, key=lambda m: m[0].user.last_name)
|
||||
# À partir d'ici, le tirage devient effectif
|
||||
# FIXME: Établir les conditions de validations (formulaire ?)
|
||||
# cf. issue #32
|
||||
if False:
|
||||
Attribution.objects.all().delete()
|
||||
for (show, members, _) in results:
|
||||
for (member, _, _, _) in members:
|
||||
attrib = Attribution(spectacle = show, participant = member)
|
||||
attrib = Attribution(spectacle=show, participant=member)
|
||||
attrib.save()
|
||||
return render(request, "bda-attrib-extra.html", data)
|
||||
else:
|
||||
return render(request, "bda-attrib.html", data)
|
||||
|
||||
@login_required
|
||||
def tirage(request):
|
||||
@buro_required
|
||||
def tirage(request, tirage_id):
|
||||
if request.POST:
|
||||
form = TokenForm(request.POST)
|
||||
if form.is_valid():
|
||||
return do_tirage(request)
|
||||
return do_tirage(request, tirage_id)
|
||||
else:
|
||||
form = TokenForm()
|
||||
return render(request, "bda-token.html", {"form": form})
|
||||
|
@ -220,15 +279,19 @@ def do_resell(request, form):
|
|||
Je souhaite revendre %s pour %s le %s (%s) à %.02f€.
|
||||
Contactez moi par email si vous êtes intéressé·e·s !
|
||||
|
||||
%s (%s)""" % (places, spectacle.title, spectacle.date_no_seconds(), spectacle.location, spectacle.price, request.user.get_full_name(), request.user.email)
|
||||
%s (%s)""" % (places, spectacle.title, spectacle.date_no_seconds(),
|
||||
spectacle.location, spectacle.price, request.user.get_full_name(),
|
||||
request.user.email)
|
||||
send_mail("%s" % spectacle, mail,
|
||||
request.user.email, ["bda-revente@lists.ens.fr"],
|
||||
fail_silently = False)
|
||||
return render(request, "bda-success.html", {"show": spectacle, "places": places})
|
||||
|
||||
@login_required
|
||||
def revente(request):
|
||||
participant, created = Participant.objects.get_or_create(user = request.user)
|
||||
def revente(request, tirage_id):
|
||||
tirage = get_object_or_404(Tirage, id=tirage_id)
|
||||
participant, created = Participant.objects.get_or_create(
|
||||
user=request.user, tirage=tirage)
|
||||
if not participant.paid:
|
||||
return render(request, "bda-notpaid.html", {})
|
||||
if request.POST:
|
||||
|
@ -237,21 +300,40 @@ def revente(request):
|
|||
return do_resell(request, form)
|
||||
else:
|
||||
form = ResellForm(participant)
|
||||
return render(request, "bda-revente.html", {"form": form})
|
||||
return render(request, "bda-revente.html", {"form": form, 'tirage': tirage})
|
||||
|
||||
@buro_required
|
||||
def spectacle(request, spectacle_id):
|
||||
spectacle = get_object_or_404(Spectacle, id = spectacle_id)
|
||||
def spectacle(request, tirage_id, spectacle_id):
|
||||
tirage = get_object_or_404(Tirage, id=tirage_id)
|
||||
spectacle = get_object_or_404(Spectacle, id = spectacle_id, tirage=tirage)
|
||||
return render(request, "bda-emails.html", {"spectacle": spectacle})
|
||||
|
||||
|
||||
class SpectacleListView(ListView):
|
||||
model = Spectacle
|
||||
template_name = 'spectacle_list.html'
|
||||
def get_queryset(self):
|
||||
self.tirage = get_object_or_404(Tirage, id=self.kwargs['tirage_id'])
|
||||
categories = self.tirage.spectacle_set
|
||||
return categories
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(SpectacleListView, self).get_context_data(**kwargs)
|
||||
context['tirage_id'] = self.tirage.id
|
||||
return context
|
||||
|
||||
@buro_required
|
||||
def unpaid(request):
|
||||
return render(request, "bda-unpaid.html", {"unpaid": Participant.objects.filter(paid = False).all()})
|
||||
def unpaid(request, tirage_id):
|
||||
tirage = get_object_or_404(Tirage, id=tirage_id)
|
||||
unpaid = tirage.participant_set.filter(paid=False).all()
|
||||
return render(request, "bda-unpaid.html", {"unpaid": unpaid})
|
||||
|
||||
@buro_required
|
||||
def liste_spectacles_ics(request):
|
||||
spectacles = Spectacle.objects.order_by("date").all()
|
||||
def liste_spectacles_ics(request, tirage_id):
|
||||
tirage = get_object_or_404(Tirage, id=tirage_id)
|
||||
spectacles = tirage.spectacle_set.order_by("date").all()
|
||||
for spectacle in spectacles:
|
||||
spectacle.dtend = spectacle.date + timedelta(seconds=7200)
|
||||
return render(request, "liste_spectacles.ics",
|
||||
{"spectacles": spectacles}, content_type="text/calendar")
|
||||
{"spectacles": spectacles, "tirage": tirage},
|
||||
content_type="text/calendar")
|
||||
|
||||
|
|
177
bda2/admin.py
177
bda2/admin.py
|
@ -1,177 +0,0 @@
|
|||
# coding: utf-8
|
||||
|
||||
from django.core.mail import send_mail
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from django.contrib import admin
|
||||
from django.db.models import Sum, Count
|
||||
from bda2.models import Spectacle, Salle, Participant, ChoixSpectacle, Attribution
|
||||
|
||||
class ChoixSpectacleInline(admin.TabularInline):
|
||||
model = ChoixSpectacle
|
||||
sortable_field_name = "priority"
|
||||
|
||||
class AttributionInline(admin.TabularInline):
|
||||
model = Attribution
|
||||
|
||||
class ParticipantAdmin(admin.ModelAdmin):
|
||||
#inlines = [ChoixSpectacleInline]
|
||||
inlines = [AttributionInline]
|
||||
def get_queryset(self, request):
|
||||
return Participant.objects.annotate(nb_places = Count('attributions'),
|
||||
total = Sum('attributions__price'))
|
||||
def nb_places(self, obj):
|
||||
return obj.nb_places
|
||||
nb_places.admin_order_field = "nb_places"
|
||||
nb_places.short_description = "Nombre de places"
|
||||
def total(self, obj):
|
||||
tot = obj.total
|
||||
if tot: return u"%.02f €" % tot
|
||||
else: return u"0 €"
|
||||
total.admin_order_field = "total"
|
||||
total.short_description = "Total à payer"
|
||||
list_display = ("user", "nb_places", "total", "paid", "paymenttype")
|
||||
list_filter = ("paid",)
|
||||
search_fields = ('user__username', 'user__first_name', 'user__last_name')
|
||||
actions = ['send_attribs',]
|
||||
actions_on_bottom = True
|
||||
list_per_page = 400
|
||||
|
||||
def send_choices(self, request, queryset):
|
||||
for member in queryset.all():
|
||||
choices = member.choixspectacle_set.order_by('priority').all()
|
||||
if len(choices) == 0:
|
||||
continue
|
||||
mail = u"""Cher(e) %s,
|
||||
Voici tes choix de spectacles tels que notre système les a enregistrés :\n\n""" % member.user.get_full_name()
|
||||
next_rank = 1
|
||||
member_shows = {}
|
||||
for choice in choices:
|
||||
if choice.spectacle in member_shows: continue
|
||||
else: member_shows[choice.spectacle] = True
|
||||
extra = ""
|
||||
if choice.double:
|
||||
extra += u" ; deux places"
|
||||
if choice.autoquit:
|
||||
extra += u" ; désistement automatique"
|
||||
mail += u"- Choix %d : %s%s\n" % (next_rank, choice.spectacle, extra)
|
||||
next_rank += 1
|
||||
mail += u"""\nSi cette liste est incorrecte, merci de nous contacter au plus vite (avant samedi 6 octobre 18h).
|
||||
|
||||
Artistiquement,
|
||||
Le BdA"""
|
||||
send_mail ("Choix de spectacles (BdA du COF)", mail,
|
||||
"bda@ens.fr", [member.user.email],
|
||||
fail_silently = True)
|
||||
count = len(queryset.all())
|
||||
if count == 1:
|
||||
message_bit = u"1 membre a"
|
||||
plural = ""
|
||||
else:
|
||||
message_bit = u"%d membres ont" % count
|
||||
plural = "s"
|
||||
self.message_user(request, u"%s été informé%s avec succès." % (message_bit, plural))
|
||||
send_choices.short_description = u"Envoyer les choix par mail"
|
||||
|
||||
def send_attribs(self, request, queryset):
|
||||
for member in queryset.all():
|
||||
attribs = member.attributions.all()
|
||||
if len(attribs) == 0:
|
||||
mail = u"""Cher-e %s,
|
||||
|
||||
Tu t'es inscrit-e pour le tirage au sort du BdA. Malheureusement, tu n'as
|
||||
obtenu aucune place.
|
||||
|
||||
Nous sommes conscients que le nombre de places proposées lors de ce tirage
|
||||
au sort est très restreint, mais nous sommes limités par les quotas que
|
||||
nous imposent les théâtres.
|
||||
|
||||
Nous proposons cependant de nombreuses offres hors-tirage tout au long de
|
||||
l'année, et nous t'invitons à nous contacter si l'une d'entre elles t'intéresse !
|
||||
--
|
||||
Le (nouveau) Bureau des Arts
|
||||
(Thomas, Caroline, Antonin, Cécile, Hugo)
|
||||
|
||||
"""
|
||||
name = member.user.get_full_name()
|
||||
mail = mail % name
|
||||
else:
|
||||
mail = u"""Cher-e %s,
|
||||
|
||||
Tu t'es inscrit-e pour le tirage au sort du BdA. Tu as été sélectionné-e
|
||||
pour les spectacles suivants :
|
||||
|
||||
%s
|
||||
|
||||
Nous sommes conscients que le nombre de places proposées lors de ce tirage
|
||||
au sort est très restreint, mais nous sommes limités par les quotas que
|
||||
nous imposent les théâtres.
|
||||
|
||||
*Paiement*
|
||||
L'intégralité de ces places de spectacles est à régler à partir du lundi
|
||||
18 janvier et AVANT le vendredi 22 janvier, au bureau du COF pendant les
|
||||
heures de permanences (du lundi au vendredi entre 12h et 14h, et entre 18h
|
||||
et 20h). Des facilités de paiement sont bien évidemment possibles : nous
|
||||
pouvons ne pas encaisser le chèque immédiatement, ou bien découper votre
|
||||
paiement en deux fois. Il est possible de payer par carte, chèque ou en espèces.
|
||||
|
||||
*Mode de retrait des places*
|
||||
Lors du paiement, nous vous donnerons les places physiques que nous possédons, et vous indiquerons
|
||||
quelles places sont sur listing, à retirer le soir même de la représentation. Nous vous enverrons un mail de rappel quelques jours avant les spectacles.
|
||||
|
||||
Nous vous rappelons que l'obtention de places du BdA vous engage à
|
||||
respecter les règles de fonctionnement :
|
||||
http://www.cof.ens.fr/bda/?page_id=1370
|
||||
Le système de revente des places via les mails BdA-revente est accessible
|
||||
directement sur votre compte GestioCOF.
|
||||
|
||||
En vous souhaitant de belles représentations,
|
||||
--
|
||||
Le (nouveau) Bureau des Arts
|
||||
(Thomas, Caroline, Antonin, Cécile, Hugo)
|
||||
"""
|
||||
attribs_text = ""
|
||||
name = member.user.get_full_name()
|
||||
for attrib in attribs:
|
||||
attribs_text += u"- 1 place pour %s\n" % attrib
|
||||
mail = mail % (name, attribs_text)
|
||||
|
||||
send_mail ("[BdA] Résultats du tirage au sort", mail,
|
||||
"bda@ens.fr", [member.user.email],
|
||||
fail_silently = True)
|
||||
count = len(queryset.all())
|
||||
if count == 1:
|
||||
message_bit = u"1 membre a"
|
||||
plural = ""
|
||||
else:
|
||||
message_bit = u"%d membres ont" % count
|
||||
plural = "s"
|
||||
self.message_user(request, u"%s été informé%s avec succès." % (message_bit, plural))
|
||||
send_attribs.short_description = u"Envoyer les résultats par mail"
|
||||
|
||||
class AttributionAdmin(admin.ModelAdmin):
|
||||
def paid(self, obj):
|
||||
return obj.participant.paid
|
||||
paid.short_description = 'A payé'
|
||||
paid.boolean = True
|
||||
list_display = ("id", "spectacle", "participant", "given", "paid")
|
||||
search_fields = ('spectacle__title', 'participant__user__username', 'participant__user__first_name', 'participant__user__last_name')
|
||||
|
||||
import autocomplete_light
|
||||
class ChoixSpectacleAdmin(admin.ModelAdmin):
|
||||
form = autocomplete_light.modelform_factory(ChoixSpectacle, exclude=[])
|
||||
list_display = ("participant", "spectacle", "priority", "double", "autoquit")
|
||||
list_filter = ("double", "autoquit")
|
||||
search_fields = ('participant__user__username', 'participant__user__first_name', 'participant__user__last_name')
|
||||
|
||||
class SpectacleAdmin(admin.ModelAdmin):
|
||||
model = Spectacle
|
||||
list_display = ("title", "date", "location", "slots", "price")
|
||||
list_filter = ("location",)
|
||||
search_fields = ("title", "location__name")
|
||||
|
||||
admin.site.register(Spectacle, SpectacleAdmin)
|
||||
admin.site.register(Salle)
|
||||
admin.site.register(Participant, ParticipantAdmin)
|
||||
admin.site.register(Attribution, AttributionAdmin)
|
||||
admin.site.register(ChoixSpectacle, ChoixSpectacleAdmin)
|
|
@ -1,104 +0,0 @@
|
|||
# coding: utf-8
|
||||
|
||||
from django.conf import settings
|
||||
from django.db.models import Max
|
||||
|
||||
import random
|
||||
|
||||
class Algorithm(object):
|
||||
|
||||
shows = None
|
||||
ranks = None
|
||||
origranks = None
|
||||
double = None
|
||||
|
||||
def __init__(self, shows, members, choices):
|
||||
"""Initialisation :
|
||||
- on aggrège toutes les demandes pour chaque spectacle dans
|
||||
show.requests
|
||||
- on crée des tables de demandes pour chaque personne, afin de
|
||||
pouvoir modifier les rankings"""
|
||||
self.max_group = 2 * choices.aggregate(Max('priority'))['priority__max']
|
||||
self.shows = []
|
||||
showdict = {}
|
||||
for show in shows:
|
||||
show.nrequests = 0
|
||||
showdict[show] = show
|
||||
show.requests = []
|
||||
self.shows.append(show)
|
||||
self.ranks = {}
|
||||
self.origranks = {}
|
||||
self.choices = {}
|
||||
next_rank = {}
|
||||
member_shows = {}
|
||||
for member in members:
|
||||
self.ranks[member] = {}
|
||||
self.choices[member] = {}
|
||||
next_rank[member] = 1
|
||||
member_shows[member] = {}
|
||||
for choice in choices:
|
||||
member = choice.participant
|
||||
if choice.spectacle in member_shows[member]: continue
|
||||
else: member_shows[member][choice.spectacle] = True
|
||||
showdict[choice.spectacle].requests.append(member)
|
||||
showdict[choice.spectacle].nrequests += 2 if choice.double else 1
|
||||
self.ranks[member][choice.spectacle] = next_rank[member]
|
||||
next_rank[member] += 2 if choice.double else 1
|
||||
self.choices[member][choice.spectacle] = choice
|
||||
for member in members:
|
||||
self.origranks[member] = dict(self.ranks[member])
|
||||
|
||||
def IncrementRanks(self, member, currank, increment = 1):
|
||||
for show in self.ranks[member]:
|
||||
if self.ranks[member][show] > currank:
|
||||
self.ranks[member][show] -= increment
|
||||
|
||||
def appendResult(self, l, member, show):
|
||||
l.append((member,
|
||||
self.ranks[member][show],
|
||||
self.origranks[member][show],
|
||||
self.choices[member][show].double))
|
||||
|
||||
"""
|
||||
Pour les 2 Walkyries: c'est Sandefer
|
||||
|
||||
Pour les 4 places pour l'Oratorio c'est Maxence Arutkin
|
||||
"""
|
||||
|
||||
def __call__(self, seed):
|
||||
random.seed(seed)
|
||||
results = []
|
||||
shows = sorted(self.shows, key = lambda x: float(x.nrequests) / x.slots, reverse = True)
|
||||
for show in shows:
|
||||
# On regroupe tous les gens ayant le même rang
|
||||
groups = dict([(i, []) for i in range(1, self.max_group + 1)])
|
||||
for member in show.requests:
|
||||
if self.ranks[member][show] == 0:
|
||||
raise RuntimeError, (member, show.title)
|
||||
groups[self.ranks[member][show]].append(member)
|
||||
# On passe à l'attribution
|
||||
winners = []
|
||||
losers = []
|
||||
for i in range(1, self.max_group + 1):
|
||||
group = list(groups[i])
|
||||
random.shuffle(group)
|
||||
for member in group:
|
||||
if self.choices[member][show].double: # double
|
||||
if len(winners) + 1 < show.slots:
|
||||
self.appendResult(winners, member, show)
|
||||
self.appendResult(winners, member, show)
|
||||
elif not self.choices[member][show].autoquit and len(winners) < show.slots:
|
||||
self.appendResult(winners, member, show)
|
||||
self.appendResult(losers, member, show)
|
||||
else:
|
||||
self.appendResult(losers, member, show)
|
||||
self.appendResult(losers, member, show)
|
||||
self.IncrementRanks(member, i, 2)
|
||||
else: # simple
|
||||
if len(winners) < show.slots:
|
||||
self.appendResult(winners, member, show)
|
||||
else:
|
||||
self.appendResult(losers, member, show)
|
||||
self.IncrementRanks(member, i)
|
||||
results.append((show,winners,losers))
|
||||
return results
|
|
@ -1,9 +0,0 @@
|
|||
import autocomplete_light
|
||||
|
||||
from bda.models import Participant, Spectacle
|
||||
|
||||
autocomplete_light.register(Participant, search_fields=('user__username','user__first_name','user__last_name'),
|
||||
autocomplete_js_attributes={'placeholder': 'participant...'})
|
||||
|
||||
autocomplete_light.register(Spectacle, search_fields=('title',),
|
||||
autocomplete_js_attributes={'placeholder': 'spectacle...'})
|
421
bda2/difftobda
421
bda2/difftobda
|
@ -1,421 +0,0 @@
|
|||
Only in .: .admin.py.swp
|
||||
Binary files ../bda/__init__.pyc and ./__init__.pyc differ
|
||||
diff -p -u -r ../bda/admin.py ./admin.py
|
||||
--- ../bda/admin.py 2013-12-17 10:19:56.000000000 +0100
|
||||
+++ ./admin.py 2012-12-08 22:31:46.000000000 +0100
|
||||
@@ -4,8 +4,8 @@ from django.core.mail import send_mail
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from django.contrib import admin
|
||||
-from django.db.models import Sum, Count
|
||||
-from bda.models import Spectacle, Salle, Participant, ChoixSpectacle, Attribution
|
||||
+from django.db.models import Sum
|
||||
+from bda2.models import Spectacle, Salle, Participant, ChoixSpectacle, Attribution
|
||||
|
||||
class ChoixSpectacleInline(admin.TabularInline):
|
||||
model = ChoixSpectacle
|
||||
@@ -17,18 +17,13 @@ class AttributionInline(admin.TabularInl
|
||||
class ParticipantAdmin(admin.ModelAdmin):
|
||||
#inlines = [ChoixSpectacleInline]
|
||||
inlines = [AttributionInline]
|
||||
- def queryset(self, request):
|
||||
- return Participant.objects.annotate(nb_places = Count('attributions'),
|
||||
- total = Sum('attributions__price'))
|
||||
def nb_places(self, obj):
|
||||
- return obj.nb_places
|
||||
- nb_places.admin_order_field = "nb_places"
|
||||
+ return len(obj.attribution_set.all())
|
||||
nb_places.short_description = "Nombre de places"
|
||||
def total(self, obj):
|
||||
- tot = obj.total
|
||||
+ tot = obj.attributions.aggregate(total = Sum('price'))['total']
|
||||
if tot: return u"%.02f €" % tot
|
||||
else: return u"0 €"
|
||||
- total.admin_order_field = "total"
|
||||
total.short_description = "Total à payer"
|
||||
list_display = ("user", "nb_places", "total", "paid", "paymenttype")
|
||||
list_filter = ("paid",)
|
||||
@@ -61,7 +56,7 @@ Voici tes choix de spectacles tels que n
|
||||
Artistiquement,
|
||||
Le BdA"""
|
||||
send_mail ("Choix de spectacles (BdA du COF)", mail,
|
||||
- "bda@ens.fr", [member.user.email],
|
||||
+ "bda@clipper.ens.fr", [member.user.email],
|
||||
fail_silently = True)
|
||||
count = len(queryset.all())
|
||||
if count == 1:
|
||||
@@ -86,48 +81,41 @@ pour les spectacles suivants :
|
||||
%s
|
||||
|
||||
*Paiement*
|
||||
-L'intégralité de ces places de spectacles est à régler à partir du jeudi
|
||||
-10 octobre et AVANT le mercredi 23 octobre, au bureau du COF pendant les
|
||||
-heures de permanences (du lundi au vendredi entre 12h et 14h, et entre 18h
|
||||
-et 20h). Des facilités de paiement sont bien évidemment possibles : nous
|
||||
-pouvons ne pas encaisser le chèque immédiatement, ou bien découper votre
|
||||
-paiement en deux fois.
|
||||
+Ces spectacles sont à régler avant le lundi 17 décembre, pendant les
|
||||
+heures de permanences du COF (tous les jours de la semaine entre 12h et
|
||||
+14h, et entre 18h et 20h). Des facilités de paiement sont bien évidemment
|
||||
+possibles (encaissement échelonné des chèques).
|
||||
|
||||
*Mode de retrait des places*
|
||||
-Au moment du paiement, une enveloppe vous sera remise, contenant les
|
||||
-places pour l'Opéra de Paris, pour les premiers spectacles de la Comédie
|
||||
-française, certains spectacles du Châtelet et du Théâtre de la Ville.
|
||||
-
|
||||
-Pour les concerts Radio France, le Théâtre des Champs-Élysées, le théâtre
|
||||
-du Rond-Point, le théâtre de la Colline, le théâtre de l'Athénée, l'IRCAM,
|
||||
-la Cité de la musique et le 104, le Studio-Théâtre de la Comédie
|
||||
-française, les places seront nominatives et à retirer au théâtre le soir
|
||||
-de la représentation au moins une demi-heure avant le début du spectacle.
|
||||
-
|
||||
-Pour le théâtre de l'Odéon, la salle Richelieu le théâtre du Vieux
|
||||
-colombier de la Comédie française, certains spectacles du théâtre de la
|
||||
-Ville et du théâtre de Châtelet ainsi que pour le théâtre de Chaillot, les
|
||||
-places seront distribuées environ une semaine avant la représentation (un
|
||||
-mail vous en avertira).
|
||||
-
|
||||
-Nous vous rappelons que l'obtention de places du BdA vous engage à
|
||||
-respecter les règles de fonctionnement :
|
||||
-http://www.cof.ens.fr/bda/?page_id=1370
|
||||
-Le système de revente des places via les mails BdA-revente sera très
|
||||
-prochainement disponible, directement sur votre compte GestioCOF.
|
||||
+Pour l'Opéra de Paris, le théâtre de la Colline et le théâtre du Châtelet,
|
||||
+les places sont à retirer au COF le jour du paiement.
|
||||
+
|
||||
+Pour les concerts Radio France, le théâtre des Champs-Élysées, l'IRCAM
|
||||
+et le 104, les places seront nominatives et à retirer à la salle le soir de
|
||||
+la représentation au moins une demi-heure avant le début du spectacle.
|
||||
+
|
||||
+Pour le théâtre de l'Odéon, la Comédie Française, le théâtre de la Ville,
|
||||
+le théâtre de Chaillot, les places seront distribuées dans vos
|
||||
+casiers environ une semaine avant la représentation (un mail vous en
|
||||
+avertira).
|
||||
+
|
||||
+Dans tous les cas, n'hésitez pas à nous contacter si vous avez un doute !
|
||||
+
|
||||
+Nous profitons aussi de ce message pour vous annoncer qu'un festival de
|
||||
+courts métrages aura lieu le 21 décembre dans l'école.
|
||||
+Plus d'informations : http://www.cof.ens.fr/bda/courts.
|
||||
+
|
||||
+Culturellement vôtre,
|
||||
|
||||
-En vous souhaitant de très beaux spectacles tout au long de l'année,
|
||||
--
|
||||
-Le Bureau des Arts
|
||||
-(Chloé, Emilie, Jaime, Maxime, Olivier)
|
||||
-"""
|
||||
+Le BdA"""
|
||||
attribs_text = ""
|
||||
name = member.user.get_full_name()
|
||||
for attrib in attribs:
|
||||
attribs_text += u"- 1 place pour %s\n" % attrib
|
||||
mail = mail % (name, attribs_text)
|
||||
- send_mail ("Résultats du tirage au sort", mail,
|
||||
- "bda@ens.fr", [member.user.email],
|
||||
+ send_mail ("Places de spectacle (BdA du COF)", mail,
|
||||
+ "bda@clipper.ens.fr", [member.user.email],
|
||||
fail_silently = True)
|
||||
count = len(queryset.all())
|
||||
if count == 1:
|
||||
@@ -154,13 +142,7 @@ class ChoixSpectacleAdmin(admin.ModelAdm
|
||||
list_filter = ("double", "autoquit")
|
||||
search_fields = ('participant__user__username', 'participant__user__first_name', 'participant__user__last_name')
|
||||
|
||||
-class SpectacleAdmin(admin.ModelAdmin):
|
||||
- model = Spectacle
|
||||
- list_display = ("title", "date", "location", "slots", "price")
|
||||
- list_filter = ("location",)
|
||||
- search_fields = ("title", "location__name")
|
||||
-
|
||||
-admin.site.register(Spectacle, SpectacleAdmin)
|
||||
+admin.site.register(Spectacle)
|
||||
admin.site.register(Salle)
|
||||
admin.site.register(Participant, ParticipantAdmin)
|
||||
admin.site.register(Attribution, AttributionAdmin)
|
||||
diff -p -u -r ../bda/algorithm.py ./algorithm.py
|
||||
--- ../bda/algorithm.py 2013-10-07 14:08:44.000000000 +0200
|
||||
+++ ./algorithm.py 2012-10-31 23:01:48.000000000 +0100
|
||||
@@ -29,24 +29,25 @@ class Algorithm(object):
|
||||
self.ranks = {}
|
||||
self.origranks = {}
|
||||
self.choices = {}
|
||||
- next_rank = {}
|
||||
- member_shows = {}
|
||||
for member in members:
|
||||
- self.ranks[member] = {}
|
||||
- self.choices[member] = {}
|
||||
- next_rank[member] = 1
|
||||
- member_shows[member] = {}
|
||||
- for choice in choices:
|
||||
- member = choice.participant
|
||||
- if choice.spectacle in member_shows[member]: continue
|
||||
- else: member_shows[member][choice.spectacle] = True
|
||||
- showdict[choice.spectacle].requests.append(member)
|
||||
- showdict[choice.spectacle].nrequests += 2 if choice.double else 1
|
||||
- self.ranks[member][choice.spectacle] = next_rank[member]
|
||||
- next_rank[member] += 2 if choice.double else 1
|
||||
- self.choices[member][choice.spectacle] = choice
|
||||
- for member in members:
|
||||
- self.origranks[member] = dict(self.ranks[member])
|
||||
+ ranks = {}
|
||||
+ member_choices = {}
|
||||
+ member_shows = {}
|
||||
+ #next_priority = 1
|
||||
+ next_rank = 1
|
||||
+ for choice in member.choixspectacle_set.order_by('priority').all():
|
||||
+ if choice.spectacle in member_shows: continue
|
||||
+ else: member_shows[choice.spectacle] = True
|
||||
+ #assert choice.priority == next_priority
|
||||
+ #next_priority += 1
|
||||
+ showdict[choice.spectacle].requests.append(member)
|
||||
+ showdict[choice.spectacle].nrequests += 2 if choice.double else 1
|
||||
+ ranks[choice.spectacle] = next_rank
|
||||
+ next_rank += 2 if choice.double else 1
|
||||
+ member_choices[choice.spectacle] = choice
|
||||
+ self.ranks[member] = ranks
|
||||
+ self.choices[member] = member_choices
|
||||
+ self.origranks[member] = dict(ranks)
|
||||
|
||||
def IncrementRanks(self, member, currank, increment = 1):
|
||||
for show in self.ranks[member]:
|
||||
Only in ../bda: algorithm.pyc
|
||||
Only in .: difftobda
|
||||
diff -p -u -r ../bda/models.py ./models.py
|
||||
--- ../bda/models.py 2013-10-10 10:44:43.000000000 +0200
|
||||
+++ ./models.py 2012-10-31 23:12:56.000000000 +0100
|
||||
@@ -1,7 +1,5 @@
|
||||
# coding: utf-8
|
||||
|
||||
-import calendar
|
||||
-
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
@@ -19,7 +17,7 @@ class Spectacle (models.Model):
|
||||
date = models.DateTimeField ("Date & heure")
|
||||
location = models.ForeignKey(Salle)
|
||||
description = models.TextField ("Description", blank = True)
|
||||
- slots_description = models.TextField ("Description des places", blank = True)
|
||||
+ #slots_description = models.TextField ("Description des places", blank = True)
|
||||
price = models.FloatField("Prix d'une place", blank = True)
|
||||
slots = models.IntegerField ("Places")
|
||||
priority = models.IntegerField ("Priorité", default = 1000)
|
||||
@@ -31,14 +29,11 @@ class Spectacle (models.Model):
|
||||
def __repr__ (self):
|
||||
return u"[%s]" % self.__unicode__()
|
||||
|
||||
- def timestamp(self):
|
||||
- return "%d" % calendar.timegm(self.date.utctimetuple())
|
||||
-
|
||||
def date_no_seconds(self):
|
||||
return self.date.strftime('%d %b %Y %H:%M')
|
||||
|
||||
def __unicode__ (self):
|
||||
- return u"%s - %s, %s, %.02f€" % (self.title, self.date_no_seconds(), self.location, self.price)
|
||||
+ return u"%s - %s @ %s, %.02f€" % (self.title, self.date_no_seconds(), self.location, self.price)
|
||||
|
||||
PAYMENT_TYPES = (
|
||||
("cash",u"Cash"),
|
||||
@@ -48,7 +43,7 @@ PAYMENT_TYPES = (
|
||||
)
|
||||
|
||||
class Participant (models.Model):
|
||||
- user = models.ForeignKey(User, unique = True)
|
||||
+ user = models.ForeignKey(User, unique = True, related_name = "participants2")
|
||||
choices = models.ManyToManyField(Spectacle, through = "ChoixSpectacle", related_name = "chosen_by")
|
||||
attributions = models.ManyToManyField(Spectacle, through = "Attribution", related_name = "attributed_to")
|
||||
paid = models.BooleanField (u"A payé", default = False)
|
||||
Binary files ../bda/models.pyc and ./models.pyc differ
|
||||
diff -p -u -r ../bda/views.py ./views.py
|
||||
--- ../bda/views.py 2014-01-04 21:37:15.000000000 +0100
|
||||
+++ ./views.py 2013-02-12 22:03:38.000000000 +0100
|
||||
@@ -1,23 +1,19 @@
|
||||
# coding: utf-8
|
||||
|
||||
-from django.contrib.auth.models import User
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.db import models
|
||||
from django.http import Http404
|
||||
from django import forms
|
||||
from django.forms.models import inlineformset_factory, BaseInlineFormSet
|
||||
-from django.core import serializers
|
||||
-import hashlib
|
||||
|
||||
from django.core.mail import send_mail
|
||||
|
||||
-from datetime import datetime
|
||||
import time
|
||||
|
||||
from gestioncof.decorators import cof_required, buro_required
|
||||
-from bda.models import Spectacle, Participant, ChoixSpectacle, Attribution
|
||||
-from bda.algorithm import Algorithm
|
||||
+from bda2.models import Spectacle, Participant, ChoixSpectacle, Attribution
|
||||
+from bda2.algorithm import Algorithm
|
||||
|
||||
class BaseBdaFormSet(BaseInlineFormSet):
|
||||
def clean(self):
|
||||
@@ -37,7 +33,7 @@ class BaseBdaFormSet(BaseInlineFormSet):
|
||||
raise forms.ValidationError("Vous ne pouvez pas vous inscrire deux fois pour le même spectacle.")
|
||||
spectacles.append(spectacle)
|
||||
|
||||
-@cof_required
|
||||
+@buro_required
|
||||
def etat_places(request):
|
||||
spectacles1 = ChoixSpectacle.objects.all().values('spectacle','spectacle__title').annotate(total = models.Count('spectacle'))
|
||||
spectacles2 = ChoixSpectacle.objects.filter(double = True).all().values('spectacle','spectacle__title').annotate(total = models.Count('spectacle'))
|
||||
@@ -46,93 +42,47 @@ def etat_places(request):
|
||||
total = 0
|
||||
for spectacle in spectacles:
|
||||
spectacle.total = 0
|
||||
- spectacle.ratio = -1.0
|
||||
spectacles_dict[spectacle.id] = spectacle
|
||||
for spectacle in spectacles1:
|
||||
spectacles_dict[spectacle["spectacle"]].total += spectacle["total"]
|
||||
- spectacles_dict[spectacle["spectacle"]].ratio = spectacles_dict[spectacle["spectacle"]].total/float(spectacles_dict[spectacle["spectacle"]].slots)
|
||||
total += spectacle["total"]
|
||||
for spectacle in spectacles2:
|
||||
spectacles_dict[spectacle["spectacle"]].total += spectacle["total"]
|
||||
- spectacles_dict[spectacle["spectacle"]].ratio = spectacles_dict[spectacle["spectacle"]].total/float(spectacles_dict[spectacle["spectacle"]].slots)
|
||||
total += spectacle["total"]
|
||||
return render(request, "etat-places.html", {"spectacles": spectacles, "total": total})
|
||||
|
||||
-def _hash_queryset(queryset):
|
||||
- data = serializers.serialize("json", queryset)
|
||||
- hasher = hashlib.sha256()
|
||||
- hasher.update(data)
|
||||
- return hasher.hexdigest()
|
||||
-
|
||||
-@cof_required
|
||||
-def places(request):
|
||||
- participant, created = Participant.objects.get_or_create(user = request.user)
|
||||
- places = participant.attribution_set.order_by("spectacle__date", "spectacle").all()
|
||||
- total = sum([place.spectacle.price for place in places])
|
||||
- filtered_places = []
|
||||
- places_dict = {}
|
||||
- spectacles = []
|
||||
- dates = []
|
||||
- warning = False
|
||||
- for place in places:
|
||||
- if place.spectacle in spectacles:
|
||||
- places_dict[place.spectacle].double = True
|
||||
- else:
|
||||
- place.double = False
|
||||
- places_dict[place.spectacle] = place
|
||||
- spectacles.append(place.spectacle)
|
||||
- filtered_places.append(place)
|
||||
- date = place.spectacle.date.date()
|
||||
- if date in dates:
|
||||
- warning = True
|
||||
- else:
|
||||
- dates.append(date)
|
||||
- return render(request, "resume_places.html",
|
||||
- {"participant": participant,
|
||||
- "places": filtered_places,
|
||||
- "total": total,
|
||||
- "warning": warning})
|
||||
-
|
||||
@cof_required
|
||||
def inscription(request):
|
||||
- if datetime.now() > datetime(2013, 10, 6, 23, 59):
|
||||
- participant, created = Participant.objects.get_or_create(user = request.user)
|
||||
- choices = participant.choixspectacle_set.order_by("priority").all()
|
||||
- return render(request, "resume_inscription.html", {"error_title": "C'est fini !", "error_description": u"Tirage au sort le 7 octobre !", "choices": choices})
|
||||
+ if time.time() > 1354921200:
|
||||
+ return render(request, "error.html", {"error_title": "C'est fini !", "error_description": u"Tirage au sort le 6 octobre dans la soirée "})
|
||||
BdaFormSet = inlineformset_factory(Participant, ChoixSpectacle, fields = ("spectacle","double","autoquit","priority",), formset = BaseBdaFormSet)
|
||||
participant, created = Participant.objects.get_or_create(user = request.user)
|
||||
success = False
|
||||
- stateerror = False
|
||||
if request.method == "POST":
|
||||
- dbstate = _hash_queryset(participant.choixspectacle_set.all())
|
||||
- if "dbstate" in request.POST and dbstate != request.POST["dbstate"]:
|
||||
- stateerror = True
|
||||
+ formset = BdaFormSet(request.POST, instance = participant)
|
||||
+ if formset.is_valid():
|
||||
+ #ChoixSpectacle.objects.filter(participant = participant).delete()
|
||||
+ formset.save()
|
||||
+ success = True
|
||||
formset = BdaFormSet(instance = participant)
|
||||
- else:
|
||||
- formset = BdaFormSet(request.POST, instance = participant)
|
||||
- if formset.is_valid():
|
||||
- #ChoixSpectacle.objects.filter(participant = participant).delete()
|
||||
- formset.save()
|
||||
- success = True
|
||||
- formset = BdaFormSet(instance = participant)
|
||||
else:
|
||||
formset = BdaFormSet(instance = participant)
|
||||
- dbstate = _hash_queryset(participant.choixspectacle_set.all())
|
||||
total_price = 0
|
||||
for choice in participant.choixspectacle_set.all():
|
||||
total_price += choice.spectacle.price
|
||||
if choice.double: total_price += choice.spectacle.price
|
||||
- return render(request, "inscription-bda.html", {"formset": formset, "success": success, "total_price": total_price, "dbstate": dbstate, "stateerror": stateerror})
|
||||
+ return render(request, "inscription-bda.html", {"formset": formset, "success": success, "total_price": total_price})
|
||||
+
|
||||
+Spectacle.deficit = lambda x: (x.slots-x.nrequests)*x.price
|
||||
|
||||
def do_tirage(request):
|
||||
form = TokenForm(request.POST)
|
||||
if not form.is_valid():
|
||||
return tirage(request)
|
||||
- start = time.time()
|
||||
data = {}
|
||||
- shows = Spectacle.objects.select_related().all()
|
||||
+ shows = Spectacle.objects.all()
|
||||
members = Participant.objects.all()
|
||||
- choices = ChoixSpectacle.objects.order_by('participant', 'priority').select_related().all()
|
||||
+ choices = ChoixSpectacle.objects.all()
|
||||
algo = Algorithm(shows, members, choices)
|
||||
results = algo(form.cleaned_data["token"])
|
||||
total_slots = 0
|
||||
@@ -149,8 +99,8 @@ def do_tirage(request):
|
||||
total_sold = 0
|
||||
total_deficit = 0
|
||||
opera_deficit = 0
|
||||
- for (show, members, _) in results:
|
||||
- deficit = (show.slots - len(members)) * show.price
|
||||
+ for show in shows:
|
||||
+ deficit = show.deficit()
|
||||
total_sold += show.slots * show.price
|
||||
if deficit >= 0:
|
||||
if u"Opéra" in show.location.name:
|
||||
@@ -159,23 +109,18 @@ def do_tirage(request):
|
||||
data["total_sold"] = total_sold - total_deficit
|
||||
data["total_deficit"] = total_deficit
|
||||
data["opera_deficit"] = opera_deficit
|
||||
- data["duration"] = time.time() - start
|
||||
if request.user.is_authenticated():
|
||||
members2 = {}
|
||||
- members_uniq = {} # Participant objects are not shared accross spectacle results,
|
||||
- # So assign a single object for each Participant id
|
||||
for (show, members, _) in results:
|
||||
for (member, _, _, _) in members:
|
||||
- if member.id not in members_uniq:
|
||||
- members_uniq[member.id] = member
|
||||
+ if member not in members2:
|
||||
members2[member] = []
|
||||
member.total = 0
|
||||
- member = members_uniq[member.id]
|
||||
members2[member].append(show)
|
||||
member.total += show.price
|
||||
members2 = members2.items()
|
||||
data["members2"] = sorted(members2, key = lambda m: m[0].user.last_name)
|
||||
- if False and request.user.username in ["seguin", "harazi"]:
|
||||
+ if False and request.user.username == "seguin":
|
||||
Attribution.objects.all().delete()
|
||||
for (show, members, _) in results:
|
||||
for (member, _, _, _) in members:
|
||||
@@ -220,7 +165,7 @@ Je souhaite revendre %s pour %s le %s (%
|
||||
Contactez moi par email si vous êtes intéressés !
|
||||
|
||||
%s (%s)""" % (places, spectacle.title, spectacle.date_no_seconds(), spectacle.location, spectacle.price, request.user.get_full_name(), request.user.email)
|
||||
- send_mail("%s" % spectacle, mail,
|
||||
+ send_mail("Revente de place: %s" % spectacle, mail,
|
||||
request.user.email, ["bda-revente@lists.ens.fr"],
|
||||
fail_silently = True)
|
||||
return render(request, "bda-success.html", {"show": spectacle, "places": places})
|
||||
Only in .: views.py~
|
|
@ -1,39 +0,0 @@
|
|||
# coding: utf-8
|
||||
|
||||
from django import forms
|
||||
from django.forms.models import inlineformset_factory, BaseInlineFormSet
|
||||
from bda2.models import Spectacle, Participant, ChoixSpectacle, Attribution
|
||||
|
||||
class BaseBdaFormSet(BaseInlineFormSet):
|
||||
def clean(self):
|
||||
"""Checks that no two articles have the same title."""
|
||||
super(BaseBdaFormSet, self).clean()
|
||||
if any(self.errors):
|
||||
# Don't bother validating the formset unless each form is valid on its own
|
||||
return
|
||||
spectacles = []
|
||||
for i in range(0, self.total_form_count()):
|
||||
form = self.forms[i]
|
||||
if not form.cleaned_data:
|
||||
continue
|
||||
spectacle = form.cleaned_data['spectacle']
|
||||
delete = form.cleaned_data['DELETE']
|
||||
if not delete and spectacle in spectacles:
|
||||
raise forms.ValidationError("Vous ne pouvez pas vous inscrire deux fois pour le même spectacle.")
|
||||
spectacles.append(spectacle)
|
||||
|
||||
class TokenForm(forms.Form):
|
||||
token = forms.CharField(widget = forms.widgets.Textarea())
|
||||
|
||||
class SpectacleModelChoiceField(forms.ModelChoiceField):
|
||||
def label_from_instance(self, obj):
|
||||
return u"%s le %s (%s) à %.02f€" % (obj.title, obj.date_no_seconds(), obj.location, obj.price)
|
||||
|
||||
class ResellForm(forms.Form):
|
||||
count = forms.ChoiceField(choices = (("1","1"),("2","2"),))
|
||||
spectacle = SpectacleModelChoiceField(queryset = Spectacle.objects.none())
|
||||
|
||||
def __init__(self, participant, *args, **kwargs):
|
||||
super(ResellForm, self).__init__(*args, **kwargs)
|
||||
self.fields['spectacle'].queryset = participant.attributions.all().distinct()
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Attribution',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('given', models.BooleanField(default=False, verbose_name='Donn\xe9e')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ChoixSpectacle',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('priority', models.PositiveIntegerField(verbose_name=b'Priorit\xc3\xa9')),
|
||||
('double', models.BooleanField(default=False, verbose_name=b'Deux places<sup>1</sup>')),
|
||||
('autoquit', models.BooleanField(default=False, verbose_name=b'Abandon<sup>2</sup>')),
|
||||
],
|
||||
options={
|
||||
'ordering': ('priority',),
|
||||
'verbose_name': 'voeu',
|
||||
'verbose_name_plural': 'voeux',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Participant',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('paid', models.BooleanField(default=False, verbose_name='A pay\xe9')),
|
||||
('paymenttype', models.CharField(blank=True, max_length=6, verbose_name='Moyen de paiement', choices=[(b'cash', 'Cash'), (b'cb', b'CB'), (b'cheque', 'Ch\xe8que'), (b'autre', 'Autre')])),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Salle',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('name', models.CharField(max_length=300, verbose_name=b'Nom')),
|
||||
('address', models.TextField(verbose_name=b'Adresse')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Spectacle',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('title', models.CharField(max_length=300, verbose_name=b'Titre')),
|
||||
('date', models.DateTimeField(verbose_name=b'Date & heure')),
|
||||
('description', models.TextField(verbose_name=b'Description', blank=True)),
|
||||
('slots_description', models.TextField(verbose_name=b'Description des places', blank=True)),
|
||||
('price', models.FloatField(verbose_name=b"Prix d'une place", blank=True)),
|
||||
('slots', models.IntegerField(verbose_name=b'Places')),
|
||||
('priority', models.IntegerField(default=1000, verbose_name=b'Priorit\xc3\xa9')),
|
||||
('location', models.ForeignKey(to='bda2.Salle')),
|
||||
],
|
||||
options={
|
||||
'ordering': ('priority', 'date', 'title'),
|
||||
'verbose_name': 'Spectacle',
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='participant',
|
||||
name='attributions',
|
||||
field=models.ManyToManyField(related_name='attributed_to', through='bda2.Attribution', to='bda2.Spectacle'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='participant',
|
||||
name='choices',
|
||||
field=models.ManyToManyField(related_name='chosen_by', through='bda2.ChoixSpectacle', to='bda2.Spectacle'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='participant',
|
||||
name='user',
|
||||
field=models.OneToOneField(related_name='participants2', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='choixspectacle',
|
||||
name='participant',
|
||||
field=models.ForeignKey(to='bda2.Participant'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='choixspectacle',
|
||||
name='spectacle',
|
||||
field=models.ForeignKey(related_name='participants', to='bda2.Spectacle'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='attribution',
|
||||
name='participant',
|
||||
field=models.ForeignKey(to='bda2.Participant'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='attribution',
|
||||
name='spectacle',
|
||||
field=models.ForeignKey(related_name='attribues', to='bda2.Spectacle'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='choixspectacle',
|
||||
unique_together=set([('participant', 'spectacle')]),
|
||||
),
|
||||
]
|
|
@ -1,78 +0,0 @@
|
|||
# coding: utf-8
|
||||
|
||||
import calendar
|
||||
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.db.models.signals import post_save
|
||||
|
||||
class Salle (models.Model):
|
||||
name = models.CharField ("Nom", max_length = 300)
|
||||
address = models.TextField ("Adresse")
|
||||
|
||||
def __unicode__ (self):
|
||||
return self.name
|
||||
|
||||
class Spectacle (models.Model):
|
||||
title = models.CharField ("Titre", max_length = 300)
|
||||
date = models.DateTimeField ("Date & heure")
|
||||
location = models.ForeignKey(Salle)
|
||||
description = models.TextField ("Description", blank = True)
|
||||
slots_description = models.TextField ("Description des places", blank = True)
|
||||
price = models.FloatField("Prix d'une place", blank = True)
|
||||
slots = models.IntegerField ("Places")
|
||||
priority = models.IntegerField ("Priorité", default = 1000)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Spectacle"
|
||||
ordering = ("priority", "date","title",)
|
||||
|
||||
def __repr__ (self):
|
||||
return u"[%s]" % self.__unicode__()
|
||||
|
||||
def timestamp(self):
|
||||
return "%d" % calendar.timegm(self.date.utctimetuple())
|
||||
|
||||
def date_no_seconds(self):
|
||||
return self.date.strftime('%d %b %Y %H:%M')
|
||||
|
||||
def __unicode__ (self):
|
||||
return u"%s - %s, %s, %.02f€" % (self.title, self.date_no_seconds(), self.location, self.price)
|
||||
|
||||
PAYMENT_TYPES = (
|
||||
("cash",u"Cash"),
|
||||
("cb","CB"),
|
||||
("cheque",u"Chèque"),
|
||||
("autre",u"Autre"),
|
||||
)
|
||||
|
||||
class Participant (models.Model):
|
||||
user = models.OneToOneField(User, related_name = "participants2")
|
||||
choices = models.ManyToManyField(Spectacle, through = "ChoixSpectacle", related_name = "chosen_by")
|
||||
attributions = models.ManyToManyField(Spectacle, through = "Attribution", related_name = "attributed_to")
|
||||
paid = models.BooleanField (u"A payé", default = False)
|
||||
paymenttype = models.CharField(u"Moyen de paiement", max_length = 6, choices = PAYMENT_TYPES, blank = True)
|
||||
|
||||
def __unicode__ (self):
|
||||
return u"%s" % (self.user)
|
||||
|
||||
class ChoixSpectacle (models.Model):
|
||||
participant = models.ForeignKey(Participant)
|
||||
spectacle = models.ForeignKey(Spectacle, related_name = "participants")
|
||||
priority = models.PositiveIntegerField("Priorité")
|
||||
double = models.BooleanField("Deux places<sup>1</sup>",default=False)
|
||||
autoquit = models.BooleanField("Abandon<sup>2</sup>",default=False)
|
||||
class Meta:
|
||||
ordering = ("priority",)
|
||||
unique_together = (("participant", "spectacle",),)
|
||||
verbose_name = "voeu"
|
||||
verbose_name_plural = "voeux"
|
||||
|
||||
class Attribution (models.Model):
|
||||
participant = models.ForeignKey(Participant)
|
||||
spectacle = models.ForeignKey(Spectacle, related_name = "attribues")
|
||||
given = models.BooleanField(u"Donnée", default = False)
|
||||
|
||||
def __unicode__ (self):
|
||||
return u"%s -- %s" % (self.participant, self.spectacle)
|
|
@ -1,10 +0,0 @@
|
|||
form#tokenform {text-align: center; font-size: 2em;}
|
||||
label {margin-right: 10px; vertical-align: top;}
|
||||
form#tokenform textarea {font-size: 2em; width: 350px; height: 200px; font-family: 'Droif Serif', serif;}
|
||||
input {width: 400px; font-size: 2em;}
|
||||
ul.losers {display: inline; margin: 0; padding: 0;}
|
||||
ul.losers li {display: inline;}
|
||||
span.details {font-size: 0.7em;}
|
||||
table {border: 1px solid black; border-collapse: collapse;}
|
||||
td {border: 1px solid black; padding: 2px;}
|
||||
.attribresult {margin: 10px 0px;}
|
|
@ -1,27 +0,0 @@
|
|||
{% extends "bda-attrib.html" %}
|
||||
|
||||
{% block extracontent %}
|
||||
|
||||
<h1>Attribution (détails)</h1>
|
||||
<h2>Token : {{ token }}</h2>
|
||||
<h2>Placés : {{ total_slots }} ; Déçus : {{ total_losers }}</h2>
|
||||
|
||||
<table>
|
||||
{% for member, shows in members2 %}
|
||||
<tr>
|
||||
<td>{{ member.user.get_full_name }}</td>
|
||||
<td>{{ member.user.email }}</td>
|
||||
<td>Total: {{ member.total }}€</td>
|
||||
<td style="width: 120px;"></td>
|
||||
</tr>
|
||||
{% for show in shows %}
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>{{ show }}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endblock %}
|
|
@ -1,41 +0,0 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block extra_head %}
|
||||
<link type="text/css" rel="stylesheet" href="{% static "css/bda.css" %}" />
|
||||
{% endblock %}
|
||||
|
||||
{% block realcontent %}
|
||||
|
||||
<h1>Attribution</h1>
|
||||
<h2>Token : {{ token }}</h2>
|
||||
<h2>Placés : {{ total_slots }} ; Déçus : {{ total_losers }}</h2>
|
||||
{% if user.profile.is_buro %}<h2>Déficit total: {{ total_deficit }} €, Opéra: {{ opera_deficit }} €, Attribué: {{ total_sold }} €</h2>{% endif %}
|
||||
|
||||
{% for show, members, losers in results %}
|
||||
<div class="attribresult">
|
||||
<h2>{{ show.title }} - {{ show.date_no_seconds }} @ {{ show.location }}</h2>
|
||||
<p>
|
||||
<strong>{{ show.nrequests }} demandes pour {{ show.slots }} places</strong>
|
||||
{{ show.price }}€ par place{% if user.profile.is_buro and show.nrequests < show.slots %}, {{ show.deficit }}€ de déficit{% endif %}
|
||||
</p>
|
||||
Places :
|
||||
<ul>
|
||||
{% for member, rank, origrank, double in members %}
|
||||
<li>{{ member.user.get_full_name }} <span class="details">(souhait {{ origrank }} — rang {{ rank }})</span></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
Déçus :
|
||||
{% if not losers %}/{% else %}
|
||||
<ul class="losers">
|
||||
{% for member, rank, origrank, double in losers %}
|
||||
{% if not forloop.first %} ; {% endif %}
|
||||
<li>{{ member.user.get_full_name }} <span class="details">(souhait {{ origrank }} — rang {{ rank }})</span></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% block extracontent %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
|
@ -1,7 +0,0 @@
|
|||
{% extends "base_title.html" %}
|
||||
|
||||
{% block realcontent %}
|
||||
<h2>{{ spectacle }}</h2>
|
||||
<textarea style="width: 100%; height: 100px; margin-top: 10px;">
|
||||
{% for attrib in spectacle.attribues.all %}{{ attrib.participant.user.email }}, {% endfor %}</textarea>
|
||||
{% endblock %}
|
|
@ -1,6 +0,0 @@
|
|||
{% extends "base_title.html" %}
|
||||
|
||||
{% block realcontent %}
|
||||
<h1><strong>Nope</strong></h1>
|
||||
<p>Avant de revendre des places, il faut aller les payer !</p>
|
||||
{% endblock %}
|
|
@ -1,26 +0,0 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block extra_head %}
|
||||
<link type="text/css" rel="stylesheet" href="{% static "css/bda.css" %}" />
|
||||
{% endblock %}
|
||||
|
||||
{% block realcontent %}
|
||||
|
||||
<h1>Revente de place</h1>
|
||||
<form action="" method="post" id="resellform">
|
||||
{% csrf_token %}
|
||||
{% if form.spectacle.errors %}<ul class="errorlist"><li>Sélectionnez un spetacle</li></ul>{% endif %}
|
||||
<p>
|
||||
<code>
|
||||
Bonjour,<br />
|
||||
<br />
|
||||
Je souhaite revendre {{ form.count }} place(s) pour {{ form.spectacle }}.<br />
|
||||
Contactez-moi par email si vous êtes intéressé !<br />
|
||||
<br />
|
||||
{{ user.get_full_name }} ({{ user.email }})
|
||||
</code>
|
||||
</p>
|
||||
<input type="submit" value="Envoyer" />
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -1,12 +0,0 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block extra_head %}
|
||||
<link type="text/css" rel="stylesheet" href="{% static "css/bda.css" %}" />
|
||||
{% endblock %}
|
||||
|
||||
{% block realcontent %}
|
||||
|
||||
<h1>Revente de place</h1>
|
||||
<p class="success">Votre offre de revente de {{ places }} pour {{ show.title }} le {{ show.date_no_seconds }} ({{ show.location }}) à {{ show.price }}€ a bien été envoyée.</p>
|
||||
{% endblock %}
|
|
@ -1,13 +0,0 @@
|
|||
{% extends "base_title.html" %}
|
||||
|
||||
{% block realcontent %}
|
||||
<h2>Tirage au sort du BdA</h2>
|
||||
<form action="" method="post" id="tokenform">
|
||||
{% csrf_token %}
|
||||
<strong>La graine :</strong>
|
||||
<div>
|
||||
{{ form.token }}
|
||||
</div>
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -1,7 +0,0 @@
|
|||
{% extends "base_title.html" %}
|
||||
|
||||
{% block realcontent %}
|
||||
<h2>Impayés</h2>
|
||||
<textarea style="width: 100%; height: 100px; margin-top: 10px;">
|
||||
{% for participant in unpaid %}{{ participant.user.email }}, {% endfor %}</textarea>
|
||||
{% endblock %}
|
|
@ -1,11 +0,0 @@
|
|||
{% extends "base_title.html" %}
|
||||
|
||||
{% block realcontent %}
|
||||
<h2>Etat des inscriptions BDA</h2>
|
||||
<ul>
|
||||
{% for spectacle in spectacles %}
|
||||
<li>{{ spectacle.title }} (<span style="font-size: 0.5em;">{{ spectacle.date_no_seconds }}, {{ spectacle.location }}</span>, {{ spectacle.slots }} places) : <strong>{{ spectacle.total }} demandes</strong></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<strong>Total : <u>{{ total }} demandes</u></strong>
|
||||
{% endblock %}
|
|
@ -1,116 +0,0 @@
|
|||
{% extends "base_title.html" %}
|
||||
|
||||
{% block extra_head %}
|
||||
<link href="{{ STATIC_URL }}grappelli/jquery/ui/css/custom-theme/jquery-ui-1.8.custom.css" rel="stylesheet" type="text/css" media="screen" title="no title" charset="utf-8" />
|
||||
<script src="{{ STATIC_URL }}grappelli/jquery/jquery-1.6.2.min.js" type="text/javascript"></script>
|
||||
<script src="{{ STATIC_URL }}grappelli/jquery/ui/js/jquery-ui-1.8.15.custom.min.js" type="text/javascript"></script>
|
||||
<link href="{{ STATIC_URL }}grappelli/css/tools.css" rel="stylesheet" type="text/css" />
|
||||
<link href="{{ STATIC_URL }}grappelli/css/jquery-ui-grappelli-extensions.css" rel="stylesheet" type="text/css" />
|
||||
{% endblock %}
|
||||
|
||||
{% block realcontent %}
|
||||
<script type="text/javascript">
|
||||
var django = {
|
||||
"jQuery": jQuery.noConflict(true)
|
||||
};
|
||||
|
||||
(function($) {
|
||||
cloneMore = function(selector, type) {
|
||||
var newElement = $(selector).clone(true);
|
||||
var total = $('#id_' + type + '-TOTAL_FORMS').val();
|
||||
newElement.find(':input').each(function() {
|
||||
var name = $(this).attr('name').replace('-' + (total-1) + '-','-' + total + '-');
|
||||
var id = 'id_' + name;
|
||||
$(this).attr({'name': name, 'id': id}).val('').removeAttr('checked');
|
||||
});
|
||||
newElement.find('label').each(function() {
|
||||
var newFor = $(this).attr('for').replace('-' + (total-1) + '-','-' + total + '-');
|
||||
$(this).attr('for', newFor);
|
||||
});
|
||||
total++;
|
||||
$('#id_' + type + '-TOTAL_FORMS').val(total);
|
||||
$(selector).after(newElement);
|
||||
}
|
||||
deleteButtonHandler = function(elem) {
|
||||
elem.bind("click", function() {
|
||||
var deleteInput = $(this).prev().prev(),
|
||||
form = $(this).parents(".dynamic-form").first();
|
||||
// callback
|
||||
// toggle options.predeleteCssClass and toggle checkbox
|
||||
if (form.hasClass("has_original")) {
|
||||
form.toggleClass("predelete");
|
||||
if (deleteInput.attr("checked")) {
|
||||
deleteInput.attr("checked", false);
|
||||
} else {
|
||||
deleteInput.attr("checked", true);
|
||||
}
|
||||
}
|
||||
// callback
|
||||
});
|
||||
};
|
||||
$(document).ready(function($) {
|
||||
deleteButtonHandler($("table#bda_formset tbody.bda_formset_content").find("a.delete-handler"));
|
||||
$("table#bda_formset tbody.bda_formset_content").sortable({
|
||||
handle: "a.drag-handler",
|
||||
items: "tr",
|
||||
axis: "y",
|
||||
appendTo: 'body',
|
||||
forceHelperSize: true,
|
||||
placeholder: 'ui-sortable-placeholder',
|
||||
forcePlaceholderSize: true,
|
||||
containment: 'form#bda_form',
|
||||
tolerance: 'pointer',
|
||||
start: function(evt, ui) {
|
||||
var template = "",
|
||||
len = ui.item.children("td").length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
template += "<td style='height:" + (ui.item.outerHeight() + 12 ) + "px' class='placeholder-cell'> </td>"
|
||||
}
|
||||
template += "";
|
||||
ui.placeholder.html(template);
|
||||
},
|
||||
stop: function(evt, ui) {
|
||||
// Toggle div.table twice to remove webkits border-spacing bug
|
||||
$("table#bda_formset").toggle().toggle();
|
||||
},
|
||||
});
|
||||
$("#bda_form").bind("submit", function(){
|
||||
var sortable_field_name = "priority";
|
||||
var i = 1;
|
||||
$(".bda_formset_content").find("tr").each(function(){
|
||||
var fields = $(this).find("td :input[value]"),
|
||||
select = $(this).find("td select");
|
||||
if (select.val() && fields.serialize()) {
|
||||
$(this).find("input[name$='"+sortable_field_name+"']").val(i);
|
||||
i++;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
})(django.jQuery);
|
||||
</script>
|
||||
|
||||
<h2>Inscription au tirage au sort du BDA</h2>
|
||||
{% if success %}
|
||||
<p class="success">Votre inscription a été mise à jour avec succès !</p>
|
||||
{% endif %}
|
||||
<form id="bda_form" method="post" action="{% url 'bda-tirage-inscription' %}">
|
||||
{% csrf_token %}
|
||||
{% include "inscription-formset.html" %}
|
||||
<input type="button" class="btn-addmore" value="Ajouter un autre vœu" id="add_more">
|
||||
<script>
|
||||
django.jQuery('#add_more').click(function() {
|
||||
cloneMore('tbody.bda_formset_content tr:last-child', 'choixspectacle_set');
|
||||
});
|
||||
</script>
|
||||
<input type="submit" class="btn-submit" value="Enregistrer" />
|
||||
Prix total actuel : {{ total_price }}€
|
||||
<hr />
|
||||
<p class="footnotes">
|
||||
<sup>1</sup>: demander deux places pour ce spectable<br />
|
||||
<sup>2</sup>: abandonner une place si impossible d'en obtenir une seconde pour ce spectacle (si vous avez coché l'option <tt>Deux places</tt> pour ce spectacle)<br />
|
||||
<sup>3</sup>: cette liste de vœu est ordonnée (du plus important au moins important), pour ajuster la priorité vous pouvez déplacer chaque vœu<br />
|
||||
</p>
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -1,40 +0,0 @@
|
|||
{{ formset.non_form_errors.as_ul }}
|
||||
<table id="bda_formset" class="form">
|
||||
{{ formset.management_form }}
|
||||
{% for form in formset.forms %}
|
||||
{% if forloop.first %}
|
||||
<thead><tr>
|
||||
{% for field in form.visible_fields %}
|
||||
{% if field.name != "DELETE" and field.name != "priority" %}
|
||||
<th class="bda-field-{{ field.name }}">{{ field.label|safe|capfirst }}</th>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<th><sup>3</sup></th>
|
||||
</tr></thead>
|
||||
<tbody class="bda_formset_content">
|
||||
{% endif %}
|
||||
<tr class="{% cycle row1,row2 %} dynamic-form {% if form.instance.pk %}has_original{% endif %}">
|
||||
{% for field in form.visible_fields %}
|
||||
{% if field.name != "DELETE" and field.name != "priority" %}
|
||||
<td class="bda-field-{{ field.name }}">
|
||||
{% if forloop.first %}
|
||||
{{ form.non_field_errors }}
|
||||
{% for hidden in form.hidden_fields %}{{ hidden }}{% endfor %}
|
||||
{% endif %}
|
||||
{{ field.errors.as_ul }}
|
||||
{{ field }}
|
||||
</td>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<td class="tools-cell"><div class="tools">
|
||||
<a href="javascript://" class="icon drag-handler" title="Déplacer"></a>
|
||||
<input type="checkbox" name="{{ form.DELETE.html_name }}" style="display: none;" />
|
||||
<input type="hidden" name="{{ form.priority.html_name }}" style="{{ form.priority.value }}" />
|
||||
<a href="javascript://" class="icon delete-handler" title="Supprimer"></a>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
|
@ -1,11 +0,0 @@
|
|||
{% extends "base_title.html" %}
|
||||
|
||||
{% block realcontent %}
|
||||
<h1><strong>Spectacles</strong></h1>
|
||||
<ul>
|
||||
<li><a href="{% url 'bda2-unpaid' %}">Pas payé</a></li>
|
||||
{% for spectacle in object_list %}
|
||||
<li><a href="{% url 'bda2-spectacle' spectacle.id %}">{{ spectacle }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
|
@ -1,16 +0,0 @@
|
|||
"""
|
||||
This file demonstrates writing tests using the unittest module. These will pass
|
||||
when you run "manage.py test".
|
||||
|
||||
Replace this with more appropriate tests for your application.
|
||||
"""
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
class SimpleTest(TestCase):
|
||||
def test_basic_addition(self):
|
||||
"""
|
||||
Tests that 1 + 1 always equals 2.
|
||||
"""
|
||||
self.assertEqual(1 + 1, 2)
|
214
bda2/views.py
214
bda2/views.py
|
@ -1,214 +0,0 @@
|
|||
# coding: utf-8
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.db import models
|
||||
from django.http import Http404
|
||||
from django.core import serializers
|
||||
import hashlib
|
||||
|
||||
from django.core.mail import send_mail
|
||||
|
||||
from datetime import datetime
|
||||
import time
|
||||
|
||||
from gestioncof.decorators import cof_required, buro_required
|
||||
from bda2.models import Spectacle, Participant, ChoixSpectacle, Attribution
|
||||
from bda2.algorithm import Algorithm
|
||||
from bda2.forms import BaseBdaFormSet, TokenForm, ResellForm
|
||||
|
||||
@cof_required
|
||||
def etat_places(request):
|
||||
spectacles1 = ChoixSpectacle.objects.all().values('spectacle','spectacle__title').annotate(total = models.Count('spectacle'))
|
||||
spectacles2 = ChoixSpectacle.objects.filter(double = True).all().values('spectacle','spectacle__title').annotate(total = models.Count('spectacle'))
|
||||
spectacles = Spectacle.objects.all()
|
||||
spectacles_dict = {}
|
||||
total = 0
|
||||
for spectacle in spectacles:
|
||||
spectacle.total = 0
|
||||
spectacle.ratio = 0.0
|
||||
spectacles_dict[spectacle.id] = spectacle
|
||||
for spectacle in spectacles1:
|
||||
spectacles_dict[spectacle["spectacle"]].total += spectacle["total"]
|
||||
spectacles_dict[spectacle["spectacle"]].ratio = spectacles_dict[spectacle["spectacle"]].total/float(spectacles_dict[spectacle["spectacle"]].slots)
|
||||
total += spectacle["total"]
|
||||
for spectacle in spectacles2:
|
||||
spectacles_dict[spectacle["spectacle"]].total += spectacle["total"]
|
||||
spectacles_dict[spectacle["spectacle"]].ratio = spectacles_dict[spectacle["spectacle"]].total/float(spectacles_dict[spectacle["spectacle"]].slots)
|
||||
total += spectacle["total"]
|
||||
return render(request, "etat-places.html", {"spectacles": spectacles, "total": total})
|
||||
|
||||
def _hash_queryset(queryset):
|
||||
data = serializers.serialize("json", queryset)
|
||||
hasher = hashlib.sha256()
|
||||
hasher.update(data)
|
||||
return hasher.hexdigest()
|
||||
|
||||
@cof_required
|
||||
def places(request):
|
||||
participant, created = Participant.objects.get_or_create(user = request.user)
|
||||
places = participant.attribution_set.order_by("spectacle__date", "spectacle").all()
|
||||
total = sum([place.spectacle.price for place in places])
|
||||
filtered_places = []
|
||||
places_dict = {}
|
||||
spectacles = []
|
||||
dates = []
|
||||
warning = False
|
||||
for place in places:
|
||||
if place.spectacle in spectacles:
|
||||
places_dict[place.spectacle].double = True
|
||||
else:
|
||||
place.double = False
|
||||
places_dict[place.spectacle] = place
|
||||
spectacles.append(place.spectacle)
|
||||
filtered_places.append(place)
|
||||
date = place.spectacle.date.date()
|
||||
if date in dates:
|
||||
warning = True
|
||||
else:
|
||||
dates.append(date)
|
||||
return render(request, "resume_places.html",
|
||||
{"participant": participant,
|
||||
"places": filtered_places,
|
||||
"total": total,
|
||||
"warning": warning})
|
||||
|
||||
@cof_required
|
||||
def inscription(request):
|
||||
if datetime.now() > datetime(2016, 1, 17, 12, 00):
|
||||
participant, created = Participant.objects.get_or_create(user = request.user)
|
||||
choices = participant.choixspectacle_set.order_by("priority").all()
|
||||
return render(request, "resume_inscription.html", {"error_title": "C'est fini !", "error_description": u"Tirage au sort le 17 janvier !", "choices": choices})
|
||||
BdaFormSet = inlineformset_factory(Participant, ChoixSpectacle, fields = ("spectacle","double","autoquit","priority",), formset = BaseBdaFormSet)
|
||||
participant, created = Participant.objects.get_or_create(user = request.user)
|
||||
success = False
|
||||
stateerror = False
|
||||
if request.method == "POST":
|
||||
dbstate = _hash_queryset(participant.choixspectacle_set.all())
|
||||
if "dbstate" in request.POST and dbstate != request.POST["dbstate"]:
|
||||
stateerror = True
|
||||
formset = BdaFormSet(instance = participant)
|
||||
else:
|
||||
formset = BdaFormSet(request.POST, instance = participant)
|
||||
if formset.is_valid():
|
||||
#ChoixSpectacle.objects.filter(participant = participant).delete()
|
||||
formset.save()
|
||||
success = True
|
||||
formset = BdaFormSet(instance = participant)
|
||||
else:
|
||||
formset = BdaFormSet(instance = participant)
|
||||
dbstate = _hash_queryset(participant.choixspectacle_set.all())
|
||||
total_price = 0
|
||||
for choice in participant.choixspectacle_set.all():
|
||||
total_price += choice.spectacle.price
|
||||
if choice.double: total_price += choice.spectacle.price
|
||||
return render(request, "inscription-bda2.html", {"formset": formset, "success": success, "total_price": total_price, "dbstate": dbstate, "stateerror": stateerror})
|
||||
|
||||
def do_tirage(request):
|
||||
form = TokenForm(request.POST)
|
||||
if not form.is_valid():
|
||||
return tirage(request)
|
||||
start = time.time()
|
||||
data = {}
|
||||
shows = Spectacle.objects.select_related().all()
|
||||
members = Participant.objects.all()
|
||||
choices = ChoixSpectacle.objects.order_by('participant', 'priority').select_related().all()
|
||||
algo = Algorithm(shows, members, choices)
|
||||
results = algo(form.cleaned_data["token"])
|
||||
total_slots = 0
|
||||
total_losers = 0
|
||||
for (_, members, losers) in results:
|
||||
total_slots += len(members)
|
||||
total_losers += len(losers)
|
||||
data["total_slots"] = total_slots
|
||||
data["total_losers"] = total_losers
|
||||
data["shows"] = shows
|
||||
data["token"] = form.cleaned_data["token"]
|
||||
data["members"] = members
|
||||
data["results"] = results
|
||||
total_sold = 0
|
||||
total_deficit = 0
|
||||
opera_deficit = 0
|
||||
for (show, members, _) in results:
|
||||
deficit = (show.slots - len(members)) * show.price
|
||||
total_sold += show.slots * show.price
|
||||
if deficit >= 0:
|
||||
if u"Opéra" in show.location.name:
|
||||
opera_deficit += deficit
|
||||
total_deficit += deficit
|
||||
data["total_sold"] = total_sold - total_deficit
|
||||
data["total_deficit"] = total_deficit
|
||||
data["opera_deficit"] = opera_deficit
|
||||
data["duration"] = time.time() - start
|
||||
if request.user.is_authenticated():
|
||||
members2 = {}
|
||||
members_uniq = {} # Participant objects are not shared accross spectacle results,
|
||||
# So assign a single object for each Participant id
|
||||
for (show, members, _) in results:
|
||||
for (member, _, _, _) in members:
|
||||
if member.id not in members_uniq:
|
||||
members_uniq[member.id] = member
|
||||
members2[member] = []
|
||||
member.total = 0
|
||||
member = members_uniq[member.id]
|
||||
members2[member].append(show)
|
||||
member.total += show.price
|
||||
members2 = members2.items()
|
||||
data["members2"] = sorted(members2, key = lambda m: m[0].user.last_name)
|
||||
if False and request.user.username in ["seguin", "harazi", "fromherz", "mpepin"]:
|
||||
Attribution.objects.all().delete()
|
||||
for (show, members, _) in results:
|
||||
for (member, _, _, _) in members:
|
||||
attrib = Attribution(spectacle = show, participant = member)
|
||||
attrib.save()
|
||||
return render(request, "bda-attrib-extra.html", data)
|
||||
else:
|
||||
return render(request, "bda-attrib.html", data)
|
||||
|
||||
@login_required
|
||||
def tirage(request):
|
||||
if request.POST:
|
||||
form = TokenForm(request.POST)
|
||||
if form.is_valid():
|
||||
return do_tirage(request)
|
||||
else:
|
||||
form = TokenForm()
|
||||
return render(request, "bda-token.html", {"form": form})
|
||||
|
||||
def do_resell(request, form):
|
||||
spectacle = form.cleaned_data["spectacle"]
|
||||
count = form.cleaned_data["count"]
|
||||
places = "2 places" if count == "2" else "une place"
|
||||
mail = u"""Bonjour,
|
||||
|
||||
Je souhaite revendre %s pour %s le %s (%s) à %.02f€.
|
||||
Contactez moi par email si vous êtes intéressés !
|
||||
|
||||
%s (%s)""" % (places, spectacle.title, spectacle.date_no_seconds(), spectacle.location, spectacle.price, request.user.get_full_name(), request.user.email)
|
||||
send_mail("%s" % spectacle, mail,
|
||||
request.user.email, ["bda-revente@lists.ens.fr"],
|
||||
fail_silently = True)
|
||||
return render(request, "bda-success.html", {"show": spectacle, "places": places})
|
||||
|
||||
@login_required
|
||||
def revente(request):
|
||||
participant, created = Participant.objects.get_or_create(user = request.user)
|
||||
if not participant.paid:
|
||||
return render(request, "bda-notpaid.html", {})
|
||||
if request.POST:
|
||||
form = ResellForm(participant, request.POST)
|
||||
if form.is_valid():
|
||||
return do_resell(request, form)
|
||||
else:
|
||||
form = ResellForm(participant)
|
||||
return render(request, "bda-revente.html", {"form": form})
|
||||
|
||||
@buro_required
|
||||
def spectacle(request, spectacle_id):
|
||||
spectacle = get_object_or_404(Spectacle, id = spectacle_id)
|
||||
return render(request, "bda-emails.html", {"spectacle": spectacle})
|
||||
|
||||
@buro_required
|
||||
def unpaid(request):
|
||||
return render(request, "bda-unpaid.html", {"unpaid": Participant.objects.filter(paid = False).all()})
|
180
bda3/admin.py
180
bda3/admin.py
|
@ -1,180 +0,0 @@
|
|||
# coding: utf-8
|
||||
|
||||
from django.core.mail import send_mail
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from django.contrib import admin
|
||||
from django.db.models import Sum, Count
|
||||
from bda3.models import Spectacle, Salle, Participant, ChoixSpectacle, Attribution
|
||||
|
||||
class ChoixSpectacleInline(admin.TabularInline):
|
||||
model = ChoixSpectacle
|
||||
sortable_field_name = "priority"
|
||||
|
||||
class AttributionInline(admin.TabularInline):
|
||||
model = Attribution
|
||||
|
||||
class ParticipantAdmin(admin.ModelAdmin):
|
||||
#inlines = [ChoixSpectacleInline]
|
||||
inlines = [AttributionInline]
|
||||
def get_queryset(self, request):
|
||||
return Participant.objects.annotate(nb_places = Count('attributions'),
|
||||
total = Sum('attributions__price'))
|
||||
def nb_places(self, obj):
|
||||
return obj.nb_places
|
||||
nb_places.admin_order_field = "nb_places"
|
||||
nb_places.short_description = "Nombre de places"
|
||||
def total(self, obj):
|
||||
tot = obj.total
|
||||
if tot: return u"%.02f €" % tot
|
||||
else: return u"0 €"
|
||||
total.admin_order_field = "total"
|
||||
total.short_description = "Total à payer"
|
||||
list_display = ("user", "nb_places", "total", "paid", "paymenttype")
|
||||
list_filter = ("paid",)
|
||||
search_fields = ('user__username', 'user__first_name', 'user__last_name')
|
||||
actions = ['send_attribs',]
|
||||
actions_on_bottom = True
|
||||
list_per_page = 400
|
||||
|
||||
def send_choices(self, request, queryset):
|
||||
for member in queryset.all():
|
||||
choices = member.choixspectacle_set.order_by('priority').all()
|
||||
if len(choices) == 0:
|
||||
continue
|
||||
mail = u"""Cher(e) %s,
|
||||
Voici tes choix de spectacles tels que notre système les a enregistrés :\n\n""" % member.user.get_full_name()
|
||||
next_rank = 1
|
||||
member_shows = {}
|
||||
for choice in choices:
|
||||
if choice.spectacle in member_shows: continue
|
||||
else: member_shows[choice.spectacle] = True
|
||||
extra = ""
|
||||
if choice.double:
|
||||
extra += u" ; deux places"
|
||||
if choice.autoquit:
|
||||
extra += u" ; désistement automatique"
|
||||
mail += u"- Choix %d : %s%s\n" % (next_rank, choice.spectacle, extra)
|
||||
next_rank += 1
|
||||
mail += u"""\nSi cette liste est incorrecte, merci de nous contacter au plus vite (avant samedi 6 octobre 18h).
|
||||
|
||||
Artistiquement,
|
||||
Le BdA"""
|
||||
send_mail ("Choix de spectacles (BdA du COF)", mail,
|
||||
"bda@ens.fr", [member.user.email],
|
||||
fail_silently = True)
|
||||
count = len(queryset.all())
|
||||
if count == 1:
|
||||
message_bit = u"1 membre a"
|
||||
plural = ""
|
||||
else:
|
||||
message_bit = u"%d membres ont" % count
|
||||
plural = "s"
|
||||
self.message_user(request, u"%s été informé%s avec succès." % (message_bit, plural))
|
||||
send_choices.short_description = u"Envoyer les choix par mail"
|
||||
|
||||
def send_attribs(self, request, queryset):
|
||||
for member in queryset.all():
|
||||
attribs = member.attributions.all()
|
||||
if len(attribs) == 0:
|
||||
mail = u"""Cher-e %s,
|
||||
|
||||
Tu t'es inscrit-e pour le troisième tirage au sort du BdA. Nous avons le regret
|
||||
de t'annoncer que tu n'as pas obtenu de place.
|
||||
|
||||
Nous sommes bien conscients que le nombre de places mises en jeu était
|
||||
très restreint, mais les premier et deuxième tirages au sort comprenaient
|
||||
déjà un nombre exceptionnel de places, et nous dépendons des limites
|
||||
fixées par les théâtres partenaires pour l'obtention de places à tarif réduit.
|
||||
|
||||
Le système de revente des places via les mails BdA-revente reste disponible,
|
||||
alors surveille tes mails.
|
||||
|
||||
En vous souhaitant une bonne fin de journée,
|
||||
--
|
||||
Le Bureau des Arts
|
||||
|
||||
"""
|
||||
name = member.user.get_full_name()
|
||||
mail = mail % name
|
||||
else:
|
||||
mail = u"""Cher-e %s,
|
||||
|
||||
Tu t'es inscrit-e pour le troisième tirage au sort du BdA. Tu as été
|
||||
sélectionné-e pour les spectacles suivants :
|
||||
|
||||
%s
|
||||
|
||||
Nous sommes bien conscients que le nombre de places mises en jeu était
|
||||
très restreint, mais les premier et deuxième tirages au sort comprenaient
|
||||
déjà un nombre exceptionnel de places, et nous dépendons des limites fixées
|
||||
par les théâtres partenaires pour l'obtention de places à tarif réduit.
|
||||
|
||||
*Paiement*
|
||||
L'intégralité de ces places de spectacles est à régler à partir du lundi
|
||||
11 avril et AVANT le 16 avril, au bureau du COF pendant les
|
||||
heures de permanences (du lundi au vendredi entre 12h et 14h, et entre 18h
|
||||
et 20h). Si vous n'êtes pas disponible cette semaine, prévenez-nous par mail.
|
||||
Attention : au-delà de cette date, les places pourront être remises en jeu.
|
||||
|
||||
*Mode de retrait des places*
|
||||
Au moment du paiement, les places pour les spectacles de la Comédie-Française
|
||||
et pour la Philarmonie vous seront remises.
|
||||
|
||||
Nous vous rappelons que l'obtention de places du BdA vous engage à
|
||||
respecter les règles de fonctionnement :
|
||||
http://www.cof.ens.fr/bda/?page_id=1370
|
||||
|
||||
Le système de revente des places via les mails BdA-revente reste disponible,
|
||||
directement sur votre compte GestioCOF.
|
||||
|
||||
En vous souhaitant de très beaux spectacles,
|
||||
--
|
||||
Le Bureau des Arts
|
||||
|
||||
"""
|
||||
attribs_text = ""
|
||||
name = member.user.get_full_name()
|
||||
for attrib in attribs:
|
||||
attribs_text += u"- 1 place pour %s\n" % attrib
|
||||
mail = mail % (name, attribs_text)
|
||||
|
||||
send_mail ("[BdA] Résultats du tirage au sort", mail,
|
||||
"bda@ens.fr", [member.user.email],
|
||||
fail_silently = True)
|
||||
count = len(queryset.all())
|
||||
if count == 1:
|
||||
message_bit = u"1 membre a"
|
||||
plural = ""
|
||||
else:
|
||||
message_bit = u"%d membres ont" % count
|
||||
plural = "s"
|
||||
self.message_user(request, u"%s été informé%s avec succès." % (message_bit, plural))
|
||||
send_attribs.short_description = u"Envoyer les résultats par mail"
|
||||
|
||||
class AttributionAdmin(admin.ModelAdmin):
|
||||
def paid(self, obj):
|
||||
return obj.participant.paid
|
||||
paid.short_description = 'A payé'
|
||||
paid.boolean = True
|
||||
list_display = ("id", "spectacle", "participant", "given", "paid")
|
||||
search_fields = ('spectacle__title', 'participant__user__username', 'participant__user__first_name', 'participant__user__last_name')
|
||||
|
||||
import autocomplete_light
|
||||
class ChoixSpectacleAdmin(admin.ModelAdmin):
|
||||
form = autocomplete_light.modelform_factory(ChoixSpectacle, exclude=[])
|
||||
list_display = ("participant", "spectacle", "priority", "double", "autoquit")
|
||||
list_filter = ("double", "autoquit")
|
||||
search_fields = ('participant__user__username', 'participant__user__first_name', 'participant__user__last_name')
|
||||
|
||||
class SpectacleAdmin(admin.ModelAdmin):
|
||||
model = Spectacle
|
||||
list_display = ("title", "date", "location", "slots", "price")
|
||||
list_filter = ("location",)
|
||||
search_fields = ("title", "location__name")
|
||||
|
||||
admin.site.register(Spectacle, SpectacleAdmin)
|
||||
admin.site.register(Salle)
|
||||
admin.site.register(Participant, ParticipantAdmin)
|
||||
admin.site.register(Attribution, AttributionAdmin)
|
||||
admin.site.register(ChoixSpectacle, ChoixSpectacleAdmin)
|
|
@ -1,104 +0,0 @@
|
|||
# coding: utf-8
|
||||
|
||||
from django.conf import settings
|
||||
from django.db.models import Max
|
||||
|
||||
import random
|
||||
|
||||
class Algorithm(object):
|
||||
|
||||
shows = None
|
||||
ranks = None
|
||||
origranks = None
|
||||
double = None
|
||||
|
||||
def __init__(self, shows, members, choices):
|
||||
"""Initialisation :
|
||||
- on aggrège toutes les demandes pour chaque spectacle dans
|
||||
show.requests
|
||||
- on crée des tables de demandes pour chaque personne, afin de
|
||||
pouvoir modifier les rankings"""
|
||||
self.max_group = 2 * choices.aggregate(Max('priority'))['priority__max']
|
||||
self.shows = []
|
||||
showdict = {}
|
||||
for show in shows:
|
||||
show.nrequests = 0
|
||||
showdict[show] = show
|
||||
show.requests = []
|
||||
self.shows.append(show)
|
||||
self.ranks = {}
|
||||
self.origranks = {}
|
||||
self.choices = {}
|
||||
next_rank = {}
|
||||
member_shows = {}
|
||||
for member in members:
|
||||
self.ranks[member] = {}
|
||||
self.choices[member] = {}
|
||||
next_rank[member] = 1
|
||||
member_shows[member] = {}
|
||||
for choice in choices:
|
||||
member = choice.participant
|
||||
if choice.spectacle in member_shows[member]: continue
|
||||
else: member_shows[member][choice.spectacle] = True
|
||||
showdict[choice.spectacle].requests.append(member)
|
||||
showdict[choice.spectacle].nrequests += 2 if choice.double else 1
|
||||
self.ranks[member][choice.spectacle] = next_rank[member]
|
||||
next_rank[member] += 2 if choice.double else 1
|
||||
self.choices[member][choice.spectacle] = choice
|
||||
for member in members:
|
||||
self.origranks[member] = dict(self.ranks[member])
|
||||
|
||||
def IncrementRanks(self, member, currank, increment = 1):
|
||||
for show in self.ranks[member]:
|
||||
if self.ranks[member][show] > currank:
|
||||
self.ranks[member][show] -= increment
|
||||
|
||||
def appendResult(self, l, member, show):
|
||||
l.append((member,
|
||||
self.ranks[member][show],
|
||||
self.origranks[member][show],
|
||||
self.choices[member][show].double))
|
||||
|
||||
"""
|
||||
Pour les 2 Walkyries: c'est Sandefer
|
||||
|
||||
Pour les 4 places pour l'Oratorio c'est Maxence Arutkin
|
||||
"""
|
||||
|
||||
def __call__(self, seed):
|
||||
random.seed(seed)
|
||||
results = []
|
||||
shows = sorted(self.shows, key = lambda x: float(x.nrequests) / x.slots, reverse = True)
|
||||
for show in shows:
|
||||
# On regroupe tous les gens ayant le même rang
|
||||
groups = dict([(i, []) for i in range(1, self.max_group + 1)])
|
||||
for member in show.requests:
|
||||
if self.ranks[member][show] == 0:
|
||||
raise RuntimeError, (member, show.title)
|
||||
groups[self.ranks[member][show]].append(member)
|
||||
# On passe à l'attribution
|
||||
winners = []
|
||||
losers = []
|
||||
for i in range(1, self.max_group + 1):
|
||||
group = list(groups[i])
|
||||
random.shuffle(group)
|
||||
for member in group:
|
||||
if self.choices[member][show].double: # double
|
||||
if len(winners) + 1 < show.slots:
|
||||
self.appendResult(winners, member, show)
|
||||
self.appendResult(winners, member, show)
|
||||
elif not self.choices[member][show].autoquit and len(winners) < show.slots:
|
||||
self.appendResult(winners, member, show)
|
||||
self.appendResult(losers, member, show)
|
||||
else:
|
||||
self.appendResult(losers, member, show)
|
||||
self.appendResult(losers, member, show)
|
||||
self.IncrementRanks(member, i, 2)
|
||||
else: # simple
|
||||
if len(winners) < show.slots:
|
||||
self.appendResult(winners, member, show)
|
||||
else:
|
||||
self.appendResult(losers, member, show)
|
||||
self.IncrementRanks(member, i)
|
||||
results.append((show,winners,losers))
|
||||
return results
|
|
@ -1,9 +0,0 @@
|
|||
import autocomplete_light
|
||||
|
||||
from bda.models import Participant, Spectacle
|
||||
|
||||
autocomplete_light.register(Participant, search_fields=('user__username','user__first_name','user__last_name'),
|
||||
autocomplete_js_attributes={'placeholder': 'participant...'})
|
||||
|
||||
autocomplete_light.register(Spectacle, search_fields=('title',),
|
||||
autocomplete_js_attributes={'placeholder': 'spectacle...'})
|
421
bda3/difftobda
421
bda3/difftobda
|
@ -1,421 +0,0 @@
|
|||
Only in .: .admin.py.swp
|
||||
Binary files ../bda/__init__.pyc and ./__init__.pyc differ
|
||||
diff -p -u -r ../bda/admin.py ./admin.py
|
||||
--- ../bda/admin.py 2013-12-17 10:19:56.000000000 +0100
|
||||
+++ ./admin.py 2012-12-08 22:31:46.000000000 +0100
|
||||
@@ -4,8 +4,8 @@ from django.core.mail import send_mail
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from django.contrib import admin
|
||||
-from django.db.models import Sum, Count
|
||||
-from bda.models import Spectacle, Salle, Participant, ChoixSpectacle, Attribution
|
||||
+from django.db.models import Sum
|
||||
+from bda3.models import Spectacle, Salle, Participant, ChoixSpectacle, Attribution
|
||||
|
||||
class ChoixSpectacleInline(admin.TabularInline):
|
||||
model = ChoixSpectacle
|
||||
@@ -17,18 +17,13 @@ class AttributionInline(admin.TabularInl
|
||||
class ParticipantAdmin(admin.ModelAdmin):
|
||||
#inlines = [ChoixSpectacleInline]
|
||||
inlines = [AttributionInline]
|
||||
- def queryset(self, request):
|
||||
- return Participant.objects.annotate(nb_places = Count('attributions'),
|
||||
- total = Sum('attributions__price'))
|
||||
def nb_places(self, obj):
|
||||
- return obj.nb_places
|
||||
- nb_places.admin_order_field = "nb_places"
|
||||
+ return len(obj.attribution_set.all())
|
||||
nb_places.short_description = "Nombre de places"
|
||||
def total(self, obj):
|
||||
- tot = obj.total
|
||||
+ tot = obj.attributions.aggregate(total = Sum('price'))['total']
|
||||
if tot: return u"%.02f €" % tot
|
||||
else: return u"0 €"
|
||||
- total.admin_order_field = "total"
|
||||
total.short_description = "Total à payer"
|
||||
list_display = ("user", "nb_places", "total", "paid", "paymenttype")
|
||||
list_filter = ("paid",)
|
||||
@@ -61,7 +56,7 @@ Voici tes choix de spectacles tels que n
|
||||
Artistiquement,
|
||||
Le BdA"""
|
||||
send_mail ("Choix de spectacles (BdA du COF)", mail,
|
||||
- "bda@ens.fr", [member.user.email],
|
||||
+ "bda@clipper.ens.fr", [member.user.email],
|
||||
fail_silently = True)
|
||||
count = len(queryset.all())
|
||||
if count == 1:
|
||||
@@ -86,48 +81,41 @@ pour les spectacles suivants :
|
||||
%s
|
||||
|
||||
*Paiement*
|
||||
-L'intégralité de ces places de spectacles est à régler à partir du jeudi
|
||||
-10 octobre et AVANT le mercredi 23 octobre, au bureau du COF pendant les
|
||||
-heures de permanences (du lundi au vendredi entre 12h et 14h, et entre 18h
|
||||
-et 20h). Des facilités de paiement sont bien évidemment possibles : nous
|
||||
-pouvons ne pas encaisser le chèque immédiatement, ou bien découper votre
|
||||
-paiement en deux fois.
|
||||
+Ces spectacles sont à régler avant le lundi 17 décembre, pendant les
|
||||
+heures de permanences du COF (tous les jours de la semaine entre 12h et
|
||||
+14h, et entre 18h et 20h). Des facilités de paiement sont bien évidemment
|
||||
+possibles (encaissement échelonné des chèques).
|
||||
|
||||
*Mode de retrait des places*
|
||||
-Au moment du paiement, une enveloppe vous sera remise, contenant les
|
||||
-places pour l'Opéra de Paris, pour les premiers spectacles de la Comédie
|
||||
-française, certains spectacles du Châtelet et du Théâtre de la Ville.
|
||||
-
|
||||
-Pour les concerts Radio France, le Théâtre des Champs-Élysées, le théâtre
|
||||
-du Rond-Point, le théâtre de la Colline, le théâtre de l'Athénée, l'IRCAM,
|
||||
-la Cité de la musique et le 104, le Studio-Théâtre de la Comédie
|
||||
-française, les places seront nominatives et à retirer au théâtre le soir
|
||||
-de la représentation au moins une demi-heure avant le début du spectacle.
|
||||
-
|
||||
-Pour le théâtre de l'Odéon, la salle Richelieu le théâtre du Vieux
|
||||
-colombier de la Comédie française, certains spectacles du théâtre de la
|
||||
-Ville et du théâtre de Châtelet ainsi que pour le théâtre de Chaillot, les
|
||||
-places seront distribuées environ une semaine avant la représentation (un
|
||||
-mail vous en avertira).
|
||||
-
|
||||
-Nous vous rappelons que l'obtention de places du BdA vous engage à
|
||||
-respecter les règles de fonctionnement :
|
||||
-http://www.cof.ens.fr/bda/?page_id=1370
|
||||
-Le système de revente des places via les mails BdA-revente sera très
|
||||
-prochainement disponible, directement sur votre compte GestioCOF.
|
||||
+Pour l'Opéra de Paris, le théâtre de la Colline et le théâtre du Châtelet,
|
||||
+les places sont à retirer au COF le jour du paiement.
|
||||
+
|
||||
+Pour les concerts Radio France, le théâtre des Champs-Élysées, l'IRCAM
|
||||
+et le 104, les places seront nominatives et à retirer à la salle le soir de
|
||||
+la représentation au moins une demi-heure avant le début du spectacle.
|
||||
+
|
||||
+Pour le théâtre de l'Odéon, la Comédie Française, le théâtre de la Ville,
|
||||
+le théâtre de Chaillot, les places seront distribuées dans vos
|
||||
+casiers environ une semaine avant la représentation (un mail vous en
|
||||
+avertira).
|
||||
+
|
||||
+Dans tous les cas, n'hésitez pas à nous contacter si vous avez un doute !
|
||||
+
|
||||
+Nous profitons aussi de ce message pour vous annoncer qu'un festival de
|
||||
+courts métrages aura lieu le 21 décembre dans l'école.
|
||||
+Plus d'informations : http://www.cof.ens.fr/bda/courts.
|
||||
+
|
||||
+Culturellement vôtre,
|
||||
|
||||
-En vous souhaitant de très beaux spectacles tout au long de l'année,
|
||||
--
|
||||
-Le Bureau des Arts
|
||||
-(Chloé, Emilie, Jaime, Maxime, Olivier)
|
||||
-"""
|
||||
+Le BdA"""
|
||||
attribs_text = ""
|
||||
name = member.user.get_full_name()
|
||||
for attrib in attribs:
|
||||
attribs_text += u"- 1 place pour %s\n" % attrib
|
||||
mail = mail % (name, attribs_text)
|
||||
- send_mail ("Résultats du tirage au sort", mail,
|
||||
- "bda@ens.fr", [member.user.email],
|
||||
+ send_mail ("Places de spectacle (BdA du COF)", mail,
|
||||
+ "bda@clipper.ens.fr", [member.user.email],
|
||||
fail_silently = True)
|
||||
count = len(queryset.all())
|
||||
if count == 1:
|
||||
@@ -154,13 +142,7 @@ class ChoixSpectacleAdmin(admin.ModelAdm
|
||||
list_filter = ("double", "autoquit")
|
||||
search_fields = ('participant__user__username', 'participant__user__first_name', 'participant__user__last_name')
|
||||
|
||||
-class SpectacleAdmin(admin.ModelAdmin):
|
||||
- model = Spectacle
|
||||
- list_display = ("title", "date", "location", "slots", "price")
|
||||
- list_filter = ("location",)
|
||||
- search_fields = ("title", "location__name")
|
||||
-
|
||||
-admin.site.register(Spectacle, SpectacleAdmin)
|
||||
+admin.site.register(Spectacle)
|
||||
admin.site.register(Salle)
|
||||
admin.site.register(Participant, ParticipantAdmin)
|
||||
admin.site.register(Attribution, AttributionAdmin)
|
||||
diff -p -u -r ../bda/algorithm.py ./algorithm.py
|
||||
--- ../bda/algorithm.py 2013-10-07 14:08:44.000000000 +0200
|
||||
+++ ./algorithm.py 2012-10-31 23:01:48.000000000 +0100
|
||||
@@ -29,24 +29,25 @@ class Algorithm(object):
|
||||
self.ranks = {}
|
||||
self.origranks = {}
|
||||
self.choices = {}
|
||||
- next_rank = {}
|
||||
- member_shows = {}
|
||||
for member in members:
|
||||
- self.ranks[member] = {}
|
||||
- self.choices[member] = {}
|
||||
- next_rank[member] = 1
|
||||
- member_shows[member] = {}
|
||||
- for choice in choices:
|
||||
- member = choice.participant
|
||||
- if choice.spectacle in member_shows[member]: continue
|
||||
- else: member_shows[member][choice.spectacle] = True
|
||||
- showdict[choice.spectacle].requests.append(member)
|
||||
- showdict[choice.spectacle].nrequests += 2 if choice.double else 1
|
||||
- self.ranks[member][choice.spectacle] = next_rank[member]
|
||||
- next_rank[member] += 2 if choice.double else 1
|
||||
- self.choices[member][choice.spectacle] = choice
|
||||
- for member in members:
|
||||
- self.origranks[member] = dict(self.ranks[member])
|
||||
+ ranks = {}
|
||||
+ member_choices = {}
|
||||
+ member_shows = {}
|
||||
+ #next_priority = 1
|
||||
+ next_rank = 1
|
||||
+ for choice in member.choixspectacle_set.order_by('priority').all():
|
||||
+ if choice.spectacle in member_shows: continue
|
||||
+ else: member_shows[choice.spectacle] = True
|
||||
+ #assert choice.priority == next_priority
|
||||
+ #next_priority += 1
|
||||
+ showdict[choice.spectacle].requests.append(member)
|
||||
+ showdict[choice.spectacle].nrequests += 2 if choice.double else 1
|
||||
+ ranks[choice.spectacle] = next_rank
|
||||
+ next_rank += 2 if choice.double else 1
|
||||
+ member_choices[choice.spectacle] = choice
|
||||
+ self.ranks[member] = ranks
|
||||
+ self.choices[member] = member_choices
|
||||
+ self.origranks[member] = dict(ranks)
|
||||
|
||||
def IncrementRanks(self, member, currank, increment = 1):
|
||||
for show in self.ranks[member]:
|
||||
Only in ../bda: algorithm.pyc
|
||||
Only in .: difftobda
|
||||
diff -p -u -r ../bda/models.py ./models.py
|
||||
--- ../bda/models.py 2013-10-10 10:44:43.000000000 +0200
|
||||
+++ ./models.py 2012-10-31 23:12:56.000000000 +0100
|
||||
@@ -1,7 +1,5 @@
|
||||
# coding: utf-8
|
||||
|
||||
-import calendar
|
||||
-
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
@@ -19,7 +17,7 @@ class Spectacle (models.Model):
|
||||
date = models.DateTimeField ("Date & heure")
|
||||
location = models.ForeignKey(Salle)
|
||||
description = models.TextField ("Description", blank = True)
|
||||
- slots_description = models.TextField ("Description des places", blank = True)
|
||||
+ #slots_description = models.TextField ("Description des places", blank = True)
|
||||
price = models.FloatField("Prix d'une place", blank = True)
|
||||
slots = models.IntegerField ("Places")
|
||||
priority = models.IntegerField ("Priorité", default = 1000)
|
||||
@@ -31,14 +29,11 @@ class Spectacle (models.Model):
|
||||
def __repr__ (self):
|
||||
return u"[%s]" % self.__unicode__()
|
||||
|
||||
- def timestamp(self):
|
||||
- return "%d" % calendar.timegm(self.date.utctimetuple())
|
||||
-
|
||||
def date_no_seconds(self):
|
||||
return self.date.strftime('%d %b %Y %H:%M')
|
||||
|
||||
def __unicode__ (self):
|
||||
- return u"%s - %s, %s, %.02f€" % (self.title, self.date_no_seconds(), self.location, self.price)
|
||||
+ return u"%s - %s @ %s, %.02f€" % (self.title, self.date_no_seconds(), self.location, self.price)
|
||||
|
||||
PAYMENT_TYPES = (
|
||||
("cash",u"Cash"),
|
||||
@@ -48,7 +43,7 @@ PAYMENT_TYPES = (
|
||||
)
|
||||
|
||||
class Participant (models.Model):
|
||||
- user = models.ForeignKey(User, unique = True)
|
||||
+ user = models.ForeignKey(User, unique = True, related_name = "participants2")
|
||||
choices = models.ManyToManyField(Spectacle, through = "ChoixSpectacle", related_name = "chosen_by")
|
||||
attributions = models.ManyToManyField(Spectacle, through = "Attribution", related_name = "attributed_to")
|
||||
paid = models.BooleanField (u"A payé", default = False)
|
||||
Binary files ../bda/models.pyc and ./models.pyc differ
|
||||
diff -p -u -r ../bda/views.py ./views.py
|
||||
--- ../bda/views.py 2014-01-04 21:37:15.000000000 +0100
|
||||
+++ ./views.py 2013-02-12 22:03:38.000000000 +0100
|
||||
@@ -1,23 +1,19 @@
|
||||
# coding: utf-8
|
||||
|
||||
-from django.contrib.auth.models import User
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.db import models
|
||||
from django.http import Http404
|
||||
from django import forms
|
||||
from django.forms.models import inlineformset_factory, BaseInlineFormSet
|
||||
-from django.core import serializers
|
||||
-import hashlib
|
||||
|
||||
from django.core.mail import send_mail
|
||||
|
||||
-from datetime import datetime
|
||||
import time
|
||||
|
||||
from gestioncof.decorators import cof_required, buro_required
|
||||
-from bda.models import Spectacle, Participant, ChoixSpectacle, Attribution
|
||||
-from bda.algorithm import Algorithm
|
||||
+from bda3.models import Spectacle, Participant, ChoixSpectacle, Attribution
|
||||
+from bda3.algorithm import Algorithm
|
||||
|
||||
class BaseBdaFormSet(BaseInlineFormSet):
|
||||
def clean(self):
|
||||
@@ -37,7 +33,7 @@ class BaseBdaFormSet(BaseInlineFormSet):
|
||||
raise forms.ValidationError("Vous ne pouvez pas vous inscrire deux fois pour le même spectacle.")
|
||||
spectacles.append(spectacle)
|
||||
|
||||
-@cof_required
|
||||
+@buro_required
|
||||
def etat_places(request):
|
||||
spectacles1 = ChoixSpectacle.objects.all().values('spectacle','spectacle__title').annotate(total = models.Count('spectacle'))
|
||||
spectacles2 = ChoixSpectacle.objects.filter(double = True).all().values('spectacle','spectacle__title').annotate(total = models.Count('spectacle'))
|
||||
@@ -46,93 +42,47 @@ def etat_places(request):
|
||||
total = 0
|
||||
for spectacle in spectacles:
|
||||
spectacle.total = 0
|
||||
- spectacle.ratio = -1.0
|
||||
spectacles_dict[spectacle.id] = spectacle
|
||||
for spectacle in spectacles1:
|
||||
spectacles_dict[spectacle["spectacle"]].total += spectacle["total"]
|
||||
- spectacles_dict[spectacle["spectacle"]].ratio = spectacles_dict[spectacle["spectacle"]].total/float(spectacles_dict[spectacle["spectacle"]].slots)
|
||||
total += spectacle["total"]
|
||||
for spectacle in spectacles2:
|
||||
spectacles_dict[spectacle["spectacle"]].total += spectacle["total"]
|
||||
- spectacles_dict[spectacle["spectacle"]].ratio = spectacles_dict[spectacle["spectacle"]].total/float(spectacles_dict[spectacle["spectacle"]].slots)
|
||||
total += spectacle["total"]
|
||||
return render(request, "etat-places.html", {"spectacles": spectacles, "total": total})
|
||||
|
||||
-def _hash_queryset(queryset):
|
||||
- data = serializers.serialize("json", queryset)
|
||||
- hasher = hashlib.sha256()
|
||||
- hasher.update(data)
|
||||
- return hasher.hexdigest()
|
||||
-
|
||||
-@cof_required
|
||||
-def places(request):
|
||||
- participant, created = Participant.objects.get_or_create(user = request.user)
|
||||
- places = participant.attribution_set.order_by("spectacle__date", "spectacle").all()
|
||||
- total = sum([place.spectacle.price for place in places])
|
||||
- filtered_places = []
|
||||
- places_dict = {}
|
||||
- spectacles = []
|
||||
- dates = []
|
||||
- warning = False
|
||||
- for place in places:
|
||||
- if place.spectacle in spectacles:
|
||||
- places_dict[place.spectacle].double = True
|
||||
- else:
|
||||
- place.double = False
|
||||
- places_dict[place.spectacle] = place
|
||||
- spectacles.append(place.spectacle)
|
||||
- filtered_places.append(place)
|
||||
- date = place.spectacle.date.date()
|
||||
- if date in dates:
|
||||
- warning = True
|
||||
- else:
|
||||
- dates.append(date)
|
||||
- return render(request, "resume_places.html",
|
||||
- {"participant": participant,
|
||||
- "places": filtered_places,
|
||||
- "total": total,
|
||||
- "warning": warning})
|
||||
-
|
||||
@cof_required
|
||||
def inscription(request):
|
||||
- if datetime.now() > datetime(2013, 10, 6, 23, 59):
|
||||
- participant, created = Participant.objects.get_or_create(user = request.user)
|
||||
- choices = participant.choixspectacle_set.order_by("priority").all()
|
||||
- return render(request, "resume_inscription.html", {"error_title": "C'est fini !", "error_description": u"Tirage au sort le 7 octobre !", "choices": choices})
|
||||
+ if time.time() > 1354921200:
|
||||
+ return render(request, "error.html", {"error_title": "C'est fini !", "error_description": u"Tirage au sort le 6 octobre dans la soirée "})
|
||||
BdaFormSet = inlineformset_factory(Participant, ChoixSpectacle, fields = ("spectacle","double","autoquit","priority",), formset = BaseBdaFormSet)
|
||||
participant, created = Participant.objects.get_or_create(user = request.user)
|
||||
success = False
|
||||
- stateerror = False
|
||||
if request.method == "POST":
|
||||
- dbstate = _hash_queryset(participant.choixspectacle_set.all())
|
||||
- if "dbstate" in request.POST and dbstate != request.POST["dbstate"]:
|
||||
- stateerror = True
|
||||
+ formset = BdaFormSet(request.POST, instance = participant)
|
||||
+ if formset.is_valid():
|
||||
+ #ChoixSpectacle.objects.filter(participant = participant).delete()
|
||||
+ formset.save()
|
||||
+ success = True
|
||||
formset = BdaFormSet(instance = participant)
|
||||
- else:
|
||||
- formset = BdaFormSet(request.POST, instance = participant)
|
||||
- if formset.is_valid():
|
||||
- #ChoixSpectacle.objects.filter(participant = participant).delete()
|
||||
- formset.save()
|
||||
- success = True
|
||||
- formset = BdaFormSet(instance = participant)
|
||||
else:
|
||||
formset = BdaFormSet(instance = participant)
|
||||
- dbstate = _hash_queryset(participant.choixspectacle_set.all())
|
||||
total_price = 0
|
||||
for choice in participant.choixspectacle_set.all():
|
||||
total_price += choice.spectacle.price
|
||||
if choice.double: total_price += choice.spectacle.price
|
||||
- return render(request, "inscription-bda.html", {"formset": formset, "success": success, "total_price": total_price, "dbstate": dbstate, "stateerror": stateerror})
|
||||
+ return render(request, "inscription-bda.html", {"formset": formset, "success": success, "total_price": total_price})
|
||||
+
|
||||
+Spectacle.deficit = lambda x: (x.slots-x.nrequests)*x.price
|
||||
|
||||
def do_tirage(request):
|
||||
form = TokenForm(request.POST)
|
||||
if not form.is_valid():
|
||||
return tirage(request)
|
||||
- start = time.time()
|
||||
data = {}
|
||||
- shows = Spectacle.objects.select_related().all()
|
||||
+ shows = Spectacle.objects.all()
|
||||
members = Participant.objects.all()
|
||||
- choices = ChoixSpectacle.objects.order_by('participant', 'priority').select_related().all()
|
||||
+ choices = ChoixSpectacle.objects.all()
|
||||
algo = Algorithm(shows, members, choices)
|
||||
results = algo(form.cleaned_data["token"])
|
||||
total_slots = 0
|
||||
@@ -149,8 +99,8 @@ def do_tirage(request):
|
||||
total_sold = 0
|
||||
total_deficit = 0
|
||||
opera_deficit = 0
|
||||
- for (show, members, _) in results:
|
||||
- deficit = (show.slots - len(members)) * show.price
|
||||
+ for show in shows:
|
||||
+ deficit = show.deficit()
|
||||
total_sold += show.slots * show.price
|
||||
if deficit >= 0:
|
||||
if u"Opéra" in show.location.name:
|
||||
@@ -159,23 +109,18 @@ def do_tirage(request):
|
||||
data["total_sold"] = total_sold - total_deficit
|
||||
data["total_deficit"] = total_deficit
|
||||
data["opera_deficit"] = opera_deficit
|
||||
- data["duration"] = time.time() - start
|
||||
if request.user.is_authenticated():
|
||||
members2 = {}
|
||||
- members_uniq = {} # Participant objects are not shared accross spectacle results,
|
||||
- # So assign a single object for each Participant id
|
||||
for (show, members, _) in results:
|
||||
for (member, _, _, _) in members:
|
||||
- if member.id not in members_uniq:
|
||||
- members_uniq[member.id] = member
|
||||
+ if member not in members2:
|
||||
members2[member] = []
|
||||
member.total = 0
|
||||
- member = members_uniq[member.id]
|
||||
members2[member].append(show)
|
||||
member.total += show.price
|
||||
members2 = members2.items()
|
||||
data["members2"] = sorted(members2, key = lambda m: m[0].user.last_name)
|
||||
- if False and request.user.username in ["seguin", "harazi"]:
|
||||
+ if False and request.user.username == "seguin":
|
||||
Attribution.objects.all().delete()
|
||||
for (show, members, _) in results:
|
||||
for (member, _, _, _) in members:
|
||||
@@ -220,7 +165,7 @@ Je souhaite revendre %s pour %s le %s (%
|
||||
Contactez moi par email si vous êtes intéressés !
|
||||
|
||||
%s (%s)""" % (places, spectacle.title, spectacle.date_no_seconds(), spectacle.location, spectacle.price, request.user.get_full_name(), request.user.email)
|
||||
- send_mail("%s" % spectacle, mail,
|
||||
+ send_mail("Revente de place: %s" % spectacle, mail,
|
||||
request.user.email, ["bda-revente@lists.ens.fr"],
|
||||
fail_silently = True)
|
||||
return render(request, "bda-success.html", {"show": spectacle, "places": places})
|
||||
Only in .: views.py~
|
|
@ -1,39 +0,0 @@
|
|||
# coding: utf-8
|
||||
|
||||
from django import forms
|
||||
from django.forms.models import inlineformset_factory, BaseInlineFormSet
|
||||
from bda3.models import Spectacle, Participant, ChoixSpectacle, Attribution
|
||||
|
||||
class BaseBdaFormSet(BaseInlineFormSet):
|
||||
def clean(self):
|
||||
"""Checks that no two articles have the same title."""
|
||||
super(BaseBdaFormSet, self).clean()
|
||||
if any(self.errors):
|
||||
# Don't bother validating the formset unless each form is valid on its own
|
||||
return
|
||||
spectacles = []
|
||||
for i in range(0, self.total_form_count()):
|
||||
form = self.forms[i]
|
||||
if not form.cleaned_data:
|
||||
continue
|
||||
spectacle = form.cleaned_data['spectacle']
|
||||
delete = form.cleaned_data['DELETE']
|
||||
if not delete and spectacle in spectacles:
|
||||
raise forms.ValidationError("Vous ne pouvez pas vous inscrire deux fois pour le même spectacle.")
|
||||
spectacles.append(spectacle)
|
||||
|
||||
class TokenForm(forms.Form):
|
||||
token = forms.CharField(widget = forms.widgets.Textarea())
|
||||
|
||||
class SpectacleModelChoiceField(forms.ModelChoiceField):
|
||||
def label_from_instance(self, obj):
|
||||
return u"%s le %s (%s) à %.02f€" % (obj.title, obj.date_no_seconds(), obj.location, obj.price)
|
||||
|
||||
class ResellForm(forms.Form):
|
||||
count = forms.ChoiceField(choices = (("1","1"),("2","2"),))
|
||||
spectacle = SpectacleModelChoiceField(queryset = Spectacle.objects.none())
|
||||
|
||||
def __init__(self, participant, *args, **kwargs):
|
||||
super(ResellForm, self).__init__(*args, **kwargs)
|
||||
self.fields['spectacle'].queryset = participant.attributions.all().distinct()
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Attribution',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('given', models.BooleanField(default=False, verbose_name='Donn\xe9e')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ChoixSpectacle',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('priority', models.PositiveIntegerField(verbose_name=b'Priorit\xc3\xa9')),
|
||||
('double', models.BooleanField(default=False, verbose_name=b'Deux places<sup>1</sup>')),
|
||||
('autoquit', models.BooleanField(default=False, verbose_name=b'Abandon<sup>2</sup>')),
|
||||
],
|
||||
options={
|
||||
'ordering': ('priority',),
|
||||
'verbose_name': 'voeu',
|
||||
'verbose_name_plural': 'voeux',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Participant',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('paid', models.BooleanField(default=False, verbose_name='A pay\xe9')),
|
||||
('paymenttype', models.CharField(blank=True, max_length=6, verbose_name='Moyen de paiement', choices=[(b'cash', 'Cash'), (b'cb', b'CB'), (b'cheque', 'Ch\xe8que'), (b'autre', 'Autre')])),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Salle',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('name', models.CharField(max_length=300, verbose_name=b'Nom')),
|
||||
('address', models.TextField(verbose_name=b'Adresse')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Spectacle',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('title', models.CharField(max_length=300, verbose_name=b'Titre')),
|
||||
('date', models.DateTimeField(verbose_name=b'Date & heure')),
|
||||
('description', models.TextField(verbose_name=b'Description', blank=True)),
|
||||
('slots_description', models.TextField(verbose_name=b'Description des places', blank=True)),
|
||||
('price', models.FloatField(verbose_name=b"Prix d'une place", blank=True)),
|
||||
('slots', models.IntegerField(verbose_name=b'Places')),
|
||||
('priority', models.IntegerField(default=1000, verbose_name=b'Priorit\xc3\xa9')),
|
||||
('location', models.ForeignKey(to='bda3.Salle')),
|
||||
],
|
||||
options={
|
||||
'ordering': ('priority', 'date', 'title'),
|
||||
'verbose_name': 'Spectacle',
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='participant',
|
||||
name='attributions',
|
||||
field=models.ManyToManyField(related_name='attributed_to', through='bda3.Attribution', to='bda3.Spectacle'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='participant',
|
||||
name='choices',
|
||||
field=models.ManyToManyField(related_name='chosen_by', through='bda3.ChoixSpectacle', to='bda3.Spectacle'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='participant',
|
||||
name='user',
|
||||
field=models.OneToOneField(related_name='participants3', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='choixspectacle',
|
||||
name='participant',
|
||||
field=models.ForeignKey(to='bda3.Participant'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='choixspectacle',
|
||||
name='spectacle',
|
||||
field=models.ForeignKey(related_name='participants', to='bda3.Spectacle'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='attribution',
|
||||
name='participant',
|
||||
field=models.ForeignKey(to='bda3.Participant'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='attribution',
|
||||
name='spectacle',
|
||||
field=models.ForeignKey(related_name='attribues', to='bda3.Spectacle'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='choixspectacle',
|
||||
unique_together=set([('participant', 'spectacle')]),
|
||||
),
|
||||
]
|
|
@ -1,78 +0,0 @@
|
|||
# coding: utf-8
|
||||
|
||||
import calendar
|
||||
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.db.models.signals import post_save
|
||||
|
||||
class Salle (models.Model):
|
||||
name = models.CharField ("Nom", max_length = 300)
|
||||
address = models.TextField ("Adresse")
|
||||
|
||||
def __unicode__ (self):
|
||||
return self.name
|
||||
|
||||
class Spectacle (models.Model):
|
||||
title = models.CharField ("Titre", max_length = 300)
|
||||
date = models.DateTimeField ("Date & heure")
|
||||
location = models.ForeignKey(Salle)
|
||||
description = models.TextField ("Description", blank = True)
|
||||
slots_description = models.TextField ("Description des places", blank = True)
|
||||
price = models.FloatField("Prix d'une place", blank = True)
|
||||
slots = models.IntegerField ("Places")
|
||||
priority = models.IntegerField ("Priorité", default = 1000)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Spectacle"
|
||||
ordering = ("priority", "date","title",)
|
||||
|
||||
def __repr__ (self):
|
||||
return u"[%s]" % self.__unicode__()
|
||||
|
||||
def timestamp(self):
|
||||
return "%d" % calendar.timegm(self.date.utctimetuple())
|
||||
|
||||
def date_no_seconds(self):
|
||||
return self.date.strftime('%d %b %Y %H:%M')
|
||||
|
||||
def __unicode__ (self):
|
||||
return u"%s - %s, %s, %.02f€" % (self.title, self.date_no_seconds(), self.location, self.price)
|
||||
|
||||
PAYMENT_TYPES = (
|
||||
("cash",u"Cash"),
|
||||
("cb","CB"),
|
||||
("cheque",u"Chèque"),
|
||||
("autre",u"Autre"),
|
||||
)
|
||||
|
||||
class Participant (models.Model):
|
||||
user = models.OneToOneField(User, related_name = "participants3")
|
||||
choices = models.ManyToManyField(Spectacle, through = "ChoixSpectacle", related_name = "chosen_by")
|
||||
attributions = models.ManyToManyField(Spectacle, through = "Attribution", related_name = "attributed_to")
|
||||
paid = models.BooleanField (u"A payé", default = False)
|
||||
paymenttype = models.CharField(u"Moyen de paiement", max_length = 6, choices = PAYMENT_TYPES, blank = True)
|
||||
|
||||
def __unicode__ (self):
|
||||
return u"%s" % (self.user)
|
||||
|
||||
class ChoixSpectacle (models.Model):
|
||||
participant = models.ForeignKey(Participant)
|
||||
spectacle = models.ForeignKey(Spectacle, related_name = "participants")
|
||||
priority = models.PositiveIntegerField("Priorité")
|
||||
double = models.BooleanField("Deux places<sup>1</sup>",default=False)
|
||||
autoquit = models.BooleanField("Abandon<sup>2</sup>",default=False)
|
||||
class Meta:
|
||||
ordering = ("priority",)
|
||||
unique_together = (("participant", "spectacle",),)
|
||||
verbose_name = "voeu"
|
||||
verbose_name_plural = "voeux"
|
||||
|
||||
class Attribution (models.Model):
|
||||
participant = models.ForeignKey(Participant)
|
||||
spectacle = models.ForeignKey(Spectacle, related_name = "attribues")
|
||||
given = models.BooleanField(u"Donnée", default = False)
|
||||
|
||||
def __unicode__ (self):
|
||||
return u"%s -- %s" % (self.participant, self.spectacle)
|
|
@ -1,10 +0,0 @@
|
|||
form#tokenform {text-align: center; font-size: 2em;}
|
||||
label {margin-right: 10px; vertical-align: top;}
|
||||
form#tokenform textarea {font-size: 2em; width: 350px; height: 200px; font-family: 'Droif Serif', serif;}
|
||||
input {width: 400px; font-size: 2em;}
|
||||
ul.losers {display: inline; margin: 0; padding: 0;}
|
||||
ul.losers li {display: inline;}
|
||||
span.details {font-size: 0.7em;}
|
||||
table {border: 1px solid black; border-collapse: collapse;}
|
||||
td {border: 1px solid black; padding: 2px;}
|
||||
.attribresult {margin: 10px 0px;}
|
|
@ -1,11 +0,0 @@
|
|||
{% extends "base_title.html" %}
|
||||
|
||||
{% block realcontent %}
|
||||
<h1><strong>Spectacles</strong></h1>
|
||||
<ul>
|
||||
<li><a href="{% url 'bda3-unpaid' %}">Pas payé</a></li>
|
||||
{% for spectacle in object_list %}
|
||||
<li><a href="{% url 'bda3-spectacle' spectacle.id %}">{{ spectacle }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
|
@ -1,16 +0,0 @@
|
|||
"""
|
||||
This file demonstrates writing tests using the unittest module. These will pass
|
||||
when you run "manage.py test".
|
||||
|
||||
Replace this with more appropriate tests for your application.
|
||||
"""
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
class SimpleTest(TestCase):
|
||||
def test_basic_addition(self):
|
||||
"""
|
||||
Tests that 1 + 1 always equals 2.
|
||||
"""
|
||||
self.assertEqual(1 + 1, 2)
|
215
bda3/views.py
215
bda3/views.py
|
@ -1,215 +0,0 @@
|
|||
|
||||
# coding: utf-8
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.db import models
|
||||
from django.http import Http404
|
||||
from django.core import serializers
|
||||
import hashlib
|
||||
|
||||
from django.core.mail import send_mail
|
||||
|
||||
from datetime import datetime
|
||||
import time
|
||||
|
||||
from gestioncof.decorators import cof_required, buro_required
|
||||
from bda3.models import Spectacle, Participant, ChoixSpectacle, Attribution
|
||||
from bda3.algorithm import Algorithm
|
||||
from bda3.forms import BaseBdaFormSet, TokenForm, ResellForm
|
||||
|
||||
@cof_required
|
||||
def etat_places(request):
|
||||
spectacles1 = ChoixSpectacle.objects.all().values('spectacle','spectacle__title').annotate(total = models.Count('spectacle'))
|
||||
spectacles2 = ChoixSpectacle.objects.filter(double = True).all().values('spectacle','spectacle__title').annotate(total = models.Count('spectacle'))
|
||||
spectacles = Spectacle.objects.all()
|
||||
spectacles_dict = {}
|
||||
total = 0
|
||||
for spectacle in spectacles:
|
||||
spectacle.total = 0
|
||||
spectacle.ratio = -1.0
|
||||
spectacles_dict[spectacle.id] = spectacle
|
||||
for spectacle in spectacles1:
|
||||
spectacles_dict[spectacle["spectacle"]].total += spectacle["total"]
|
||||
spectacles_dict[spectacle["spectacle"]].ratio = spectacles_dict[spectacle["spectacle"]].total/float(spectacles_dict[spectacle["spectacle"]].slots)
|
||||
total += spectacle["total"]
|
||||
for spectacle in spectacles2:
|
||||
spectacles_dict[spectacle["spectacle"]].total += spectacle["total"]
|
||||
spectacles_dict[spectacle["spectacle"]].ratio = spectacles_dict[spectacle["spectacle"]].total/float(spectacles_dict[spectacle["spectacle"]].slots)
|
||||
total += spectacle["total"]
|
||||
return render(request, "etat-places.html", {"spectacles": spectacles, "total": total})
|
||||
|
||||
def _hash_queryset(queryset):
|
||||
data = serializers.serialize("json", queryset)
|
||||
hasher = hashlib.sha256()
|
||||
hasher.update(data)
|
||||
return hasher.hexdigest()
|
||||
|
||||
@cof_required
|
||||
def places(request):
|
||||
participant, created = Participant.objects.get_or_create(user = request.user)
|
||||
places = participant.attribution_set.order_by("spectacle__date", "spectacle").all()
|
||||
total = sum([place.spectacle.price for place in places])
|
||||
filtered_places = []
|
||||
places_dict = {}
|
||||
spectacles = []
|
||||
dates = []
|
||||
warning = False
|
||||
for place in places:
|
||||
if place.spectacle in spectacles:
|
||||
places_dict[place.spectacle].double = True
|
||||
else:
|
||||
place.double = False
|
||||
places_dict[place.spectacle] = place
|
||||
spectacles.append(place.spectacle)
|
||||
filtered_places.append(place)
|
||||
date = place.spectacle.date.date()
|
||||
if date in dates:
|
||||
warning = True
|
||||
else:
|
||||
dates.append(date)
|
||||
return render(request, "resume_places.html",
|
||||
{"participant": participant,
|
||||
"places": filtered_places,
|
||||
"total": total,
|
||||
"warning": warning})
|
||||
|
||||
@cof_required
|
||||
def inscription(request):
|
||||
if datetime.now() > datetime(2016, 4, 10, 11, 59):
|
||||
participant, created = Participant.objects.get_or_create(user = request.user)
|
||||
choices = participant.choixspectacle_set.order_by("priority").all()
|
||||
return render(request, "resume_inscription.html", {"error_title": "C'est fini !", "error_description": u"Tirage au sort très bientôt !", "choices": choices})
|
||||
BdaFormSet = inlineformset_factory(Participant, ChoixSpectacle, fields = ("spectacle","double","autoquit","priority",), formset = BaseBdaFormSet)
|
||||
participant, created = Participant.objects.get_or_create(user = request.user)
|
||||
success = False
|
||||
stateerror = False
|
||||
if request.method == "POST":
|
||||
dbstate = _hash_queryset(participant.choixspectacle_set.all())
|
||||
if "dbstate" in request.POST and dbstate != request.POST["dbstate"]:
|
||||
stateerror = True
|
||||
formset = BdaFormSet(instance = participant)
|
||||
else:
|
||||
formset = BdaFormSet(request.POST, instance = participant)
|
||||
if formset.is_valid():
|
||||
#ChoixSpectacle.objects.filter(participant = participant).delete()
|
||||
formset.save()
|
||||
success = True
|
||||
formset = BdaFormSet(instance = participant)
|
||||
else:
|
||||
formset = BdaFormSet(instance = participant)
|
||||
dbstate = _hash_queryset(participant.choixspectacle_set.all())
|
||||
total_price = 0
|
||||
for choice in participant.choixspectacle_set.all():
|
||||
total_price += choice.spectacle.price
|
||||
if choice.double: total_price += choice.spectacle.price
|
||||
return render(request, "inscription-bda3.html", {"formset": formset, "success": success, "total_price": total_price, "dbstate": dbstate, "stateerror": stateerror})
|
||||
|
||||
def do_tirage(request):
|
||||
form = TokenForm(request.POST)
|
||||
if not form.is_valid():
|
||||
return tirage(request)
|
||||
start = time.time()
|
||||
data = {}
|
||||
shows = Spectacle.objects.select_related().all()
|
||||
members = Participant.objects.all()
|
||||
choices = ChoixSpectacle.objects.order_by('participant', 'priority').select_related().all()
|
||||
algo = Algorithm(shows, members, choices)
|
||||
results = algo(form.cleaned_data["token"])
|
||||
total_slots = 0
|
||||
total_losers = 0
|
||||
for (_, members, losers) in results:
|
||||
total_slots += len(members)
|
||||
total_losers += len(losers)
|
||||
data["total_slots"] = total_slots
|
||||
data["total_losers"] = total_losers
|
||||
data["shows"] = shows
|
||||
data["token"] = form.cleaned_data["token"]
|
||||
data["members"] = members
|
||||
data["results"] = results
|
||||
total_sold = 0
|
||||
total_deficit = 0
|
||||
opera_deficit = 0
|
||||
for (show, members, _) in results:
|
||||
deficit = (show.slots - len(members)) * show.price
|
||||
total_sold += show.slots * show.price
|
||||
if deficit >= 0:
|
||||
if u"Opéra" in show.location.name:
|
||||
opera_deficit += deficit
|
||||
total_deficit += deficit
|
||||
data["total_sold"] = total_sold - total_deficit
|
||||
data["total_deficit"] = total_deficit
|
||||
data["opera_deficit"] = opera_deficit
|
||||
data["duration"] = time.time() - start
|
||||
if request.user.is_authenticated():
|
||||
members2 = {}
|
||||
members_uniq = {} # Participant objects are not shared accross spectacle results,
|
||||
# So assign a single object for each Participant id
|
||||
for (show, members, _) in results:
|
||||
for (member, _, _, _) in members:
|
||||
if member.id not in members_uniq:
|
||||
members_uniq[member.id] = member
|
||||
members2[member] = []
|
||||
member.total = 0
|
||||
member = members_uniq[member.id]
|
||||
members2[member].append(show)
|
||||
member.total += show.price
|
||||
members2 = members2.items()
|
||||
data["members2"] = sorted(members2, key = lambda m: m[0].user.last_name)
|
||||
if False and request.user.username in ["seguin", "harazi", "fromherz", "mpepin"]:
|
||||
Attribution.objects.all().delete()
|
||||
for (show, members, _) in results:
|
||||
for (member, _, _, _) in members:
|
||||
attrib = Attribution(spectacle = show, participant = member)
|
||||
attrib.save()
|
||||
return render(request, "bda-attrib-extra.html", data)
|
||||
else:
|
||||
return render(request, "bda-attrib.html", data)
|
||||
|
||||
@login_required
|
||||
def tirage(request):
|
||||
if request.POST:
|
||||
form = TokenForm(request.POST)
|
||||
if form.is_valid():
|
||||
return do_tirage(request)
|
||||
else:
|
||||
form = TokenForm()
|
||||
return render(request, "bda-token.html", {"form": form})
|
||||
|
||||
def do_resell(request, form):
|
||||
spectacle = form.cleaned_data["spectacle"]
|
||||
count = form.cleaned_data["count"]
|
||||
places = "2 places" if count == "2" else "une place"
|
||||
mail = u"""Bonjour,
|
||||
|
||||
Je souhaite revendre %s pour %s le %s (%s) à %.02f€.
|
||||
Contactez moi par email si vous êtes intéressés !
|
||||
|
||||
%s (%s)""" % (places, spectacle.title, spectacle.date_no_seconds(), spectacle.location, spectacle.price, request.user.get_full_name(), request.user.email)
|
||||
send_mail("%s" % spectacle, mail,
|
||||
request.user.email, ["bda-revente@lists.ens.fr"],
|
||||
fail_silently = True)
|
||||
return render(request, "bda-success.html", {"show": spectacle, "places": places})
|
||||
|
||||
@login_required
|
||||
def revente(request):
|
||||
participant, created = Participant.objects.get_or_create(user = request.user)
|
||||
if not participant.paid:
|
||||
return render(request, "bda-notpaid.html", {})
|
||||
if request.POST:
|
||||
form = ResellForm(participant, request.POST)
|
||||
if form.is_valid():
|
||||
return do_resell(request, form)
|
||||
else:
|
||||
form = ResellForm(participant)
|
||||
return render(request, "bda-revente.html", {"form": form})
|
||||
|
||||
@buro_required
|
||||
def spectacle(request, spectacle_id):
|
||||
spectacle = get_object_or_404(Spectacle, id = spectacle_id)
|
||||
return render(request, "bda-emails.html", {"spectacle": spectacle})
|
||||
|
||||
@buro_required
|
||||
def unpaid(request):
|
||||
return render(request, "bda-unpaid.html", {"unpaid": Participant.objects.filter(paid = False).all()})
|
|
@ -41,8 +41,6 @@ INSTALLED_APPS = (
|
|||
'django.contrib.admin',
|
||||
'django.contrib.admindocs',
|
||||
'bda',
|
||||
'bda2',
|
||||
'bda3',
|
||||
'autocomplete_light',
|
||||
'captcha',
|
||||
'django_cas_ng',
|
||||
|
|
40
cof/urls.py
40
cof/urls.py
|
@ -6,11 +6,9 @@ autocomplete_light.autodiscover()
|
|||
from django.contrib import admin
|
||||
admin.autodiscover()
|
||||
|
||||
from django.views.generic.list import ListView
|
||||
from django.views.generic.base import TemplateView
|
||||
from bda.models import Spectacle
|
||||
from bda2.models import Spectacle as Spectacle2
|
||||
from bda3.models import Spectacle as Spectacle3
|
||||
from bda.views import SpectacleListView
|
||||
from gestioncof.petits_cours_views import DemandeListView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
|
@ -42,32 +40,16 @@ urlpatterns = patterns('',
|
|||
url(r'^petitcours/demandes/(?P<demande_id>\d+)$', 'gestioncof.petits_cours_views.details', name = 'petits-cours-demande-details'),
|
||||
url(r'^petitcours/demandes/(?P<demande_id>\d+)/traitement$', 'gestioncof.petits_cours_views.traitement', name = 'petits-cours-demande-traitement'),
|
||||
url(r'^petitcours/demandes/(?P<demande_id>\d+)/retraitement$', 'gestioncof.petits_cours_views.retraitement', name = 'petits-cours-demande-retraitement'),
|
||||
url(r'^bda/inscription$', 'bda.views.inscription', name = 'bda-tirage-inscription'),
|
||||
url(r'^bda2/inscription$', 'bda2.views.inscription', name = 'bda2-tirage-inscription'),
|
||||
url(r'^bda3/inscription$', 'bda3.views.inscription', name = 'bda3-tirage-inscription'),
|
||||
url(r'^bda/places$', 'bda.views.places', name = "bda-places-attribuees"),
|
||||
url(r'^bda/places/places_bda.ics$', 'bda.views.places_ics', name = "bda-places-attribuees-ics"),
|
||||
url(r'^bda2/places$', 'bda2.views.places', name = "bda2-places-attribuees"),
|
||||
url(r'^bda3/places$', 'bda3.views.places', name = "bda3-places-attribuees"),
|
||||
url(r'^bda/revente$', 'bda.views.revente', name = 'bda-revente'),
|
||||
url(r'^bda2/revente$', 'bda2.views.revente', name = 'bda2-revente'),
|
||||
url(r'^bda3/revente$', 'bda3.views.revente', name = 'bda3-revente'),
|
||||
url(r'^bda/etat-places$', 'bda.views.etat_places', name = 'bda-etat-places'),
|
||||
url(r'^bda2/etat-places$', 'bda2.views.etat_places', name = 'bda2-etat-places'),
|
||||
url(r'^bda3/etat-places$', 'bda3.views.etat_places', name = 'bda3-etat-places'),
|
||||
url(r'^bda/tirage$', 'bda.views.tirage'),
|
||||
url(r'^bda2/tirage$', 'bda2.views.tirage'),
|
||||
url(r'^bda3/tirage$', 'bda3.views.tirage'),
|
||||
url(r'^bda/spectacles/$', ListView.as_view(model = Spectacle), name ="bda-liste-spectacles"),
|
||||
url(r'^bda/spectacles/liste_spectacles.ics$', 'bda.views.liste_spectacles_ics', name ="bda-liste-spectacles-ics"),
|
||||
url(r'^bda/spectacles/unpaid$', "bda.views.unpaid", name = "bda-unpaid"),
|
||||
url(r'^bda/spectacles/(?P<spectacle_id>\d+)$', "bda.views.spectacle", name = "bda-spectacle"),
|
||||
url(r'^bda/spectacles-2/$', ListView.as_view(model = Spectacle2), name ="bda2-liste-spectacles"),
|
||||
url(r'^bda/spectacles-2/unpaid$', "bda2.views.unpaid", name = "bda2-unpaid"),
|
||||
url(r'^bda/spectacles-2/(?P<spectacle_id>\d+)$', "bda2.views.spectacle", name = "bda2-spectacle"),
|
||||
url(r'^bda/spectacles-3/$', ListView.as_view(model = Spectacle3), name ="bda3-liste-spectacles"),
|
||||
url(r'^bda/spectacles-3/unpaid$', "bda3.views.unpaid", name = "bda3-unpaid"),
|
||||
url(r'^bda/spectacles-3/(?P<spectacle_id>\d+)$', "bda3.views.spectacle", name = "bda3-spectacle"),
|
||||
url(r'^bda/inscription/(?P<tirage_id>\d+)$', 'bda.views.inscription', name = 'bda-tirage-inscription'),
|
||||
url(r'^bda/places/(?P<tirage_id>\d+)$', 'bda.views.places', name = "bda-places-attribuees"),
|
||||
url(r'^bda/places/(?P<tirage_id>\d+)/places_bda.ics$', 'bda.views.places_ics', name = "bda-places-attribuees-ics"),
|
||||
url(r'^bda/revente/(?P<tirage_id>\d+)$', 'bda.views.revente', name = 'bda-revente'),
|
||||
url(r'^bda/etat-places/(?P<tirage_id>\d+)$', 'bda.views.etat_places', name = 'bda-etat-places'),
|
||||
url(r'^bda/tirage/(?P<tirage_id>\d+)$', 'bda.views.tirage'),
|
||||
url(r'^bda/spectacles/(?P<tirage_id>\d+)$', SpectacleListView.as_view() , name ="bda-liste-spectacles"),
|
||||
url(r'^bda/spectacles/(?P<tirage_id>\d+)/(?P<spectacle_id>\d+)$', "bda.views.spectacle", name = "bda-spectacle"),
|
||||
url(r'^bda/spectacles-ics/(?P<tirage_id>\d+)$', 'bda.views.liste_spectacles_ics', name ="bda-liste-spectacles-ics"),
|
||||
url(r'^bda/spectacles/unpaid/(?P<tirage_id>\d+)$', "bda.views.unpaid", name = "bda-unpaid"),
|
||||
url(r'^survey/(?P<survey_id>\d+)$', 'gestioncof.views.survey'),
|
||||
url(r'^event/(?P<event_id>\d+)$', 'gestioncof.views.event'),
|
||||
url(r'^survey/(?P<survey_id>\d+)/status$', 'gestioncof.views.survey_status'),
|
||||
|
|
|
@ -21,62 +21,52 @@
|
|||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if user.profile.is_cof %}
|
||||
<h3>BdA</h3>
|
||||
<ul>
|
||||
<!-- <li><a href="{% url "bda-tirage-inscription" %}">Inscription au premier tirage au sort du BdA</a></li>
|
||||
<li><a href="{% url "bda-etat-places" %}">État des demandes</a>-->
|
||||
<li><a href="{% url "bda-places-attribuees" %}">Mes places du premier tirage</a></li>
|
||||
<li><a href="{% url "bda-revente" %}">Revendre une place du premier tirage</a></li>
|
||||
<br>
|
||||
Second tirage
|
||||
<!-- <li><a href="{% url "bda2-tirage-inscription" %}">Inscription au deuxième tirage au sort du BdA</a></li>
|
||||
<li><a href="{% url "bda2-etat-places" %}">État des demandes</a></li> -->
|
||||
<li><a href="{% url "bda2-places-attribuees" %}">Mes places du deuxième tirage</a></li>
|
||||
<li><a href="{% url "bda2-revente" %}">Revendre une place du deuxième tirage</a></li>
|
||||
<br>
|
||||
|
||||
|
||||
Troisième tirage
|
||||
<!--<li><a href="{% url "bda3-tirage-inscription" %}">Inscription au troisième tirage au sort du BdA</a></li>
|
||||
<li><a href="{% url "bda3-etat-places" %}">État des demandes</a></li>-->
|
||||
<li><a href="{% url "bda3-places-attribuees" %}">Mes places du troisième tirage</a></li>
|
||||
<li><a href="{% url "bda3-revente" %}">Revendre une place du troisième tirage</a></li>
|
||||
<br>
|
||||
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if user.profile.is_cof %}
|
||||
{% if open_tirages %}
|
||||
<h3>Tirages du BdA</h3>
|
||||
{% for tirage in open_tirages %}
|
||||
<ul>
|
||||
{{ tirage.title }}
|
||||
<li><a href="{% url "bda-tirage-inscription" tirage.id %}">Inscription</a></li>
|
||||
<li><a href="{% url "bda-etat-places" tirage.id %}">État des demandes</a>
|
||||
<li><a href="{% url "bda-places-attribuees" tirage.id %}">Mes places</a></li>
|
||||
<li><a href="{% url "bda-revente" tirage.id %}">Revendre une place</a></li>
|
||||
<br>
|
||||
</ul>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<h3>Divers</h3>
|
||||
<ul>
|
||||
|
||||
{% if user.profile.is_cof %}<li><a href="{% url "petits-cours-inscription" %}">Inscription pour donner des petits cours</a></li>{% endif %}
|
||||
|
||||
<li><a href="{% url "gestioncof.views.profile" %}">Éditer mon profil</a></li>
|
||||
<li><a href="{% url "gestioncof.views.profile" %}">Éditer mon profil</a></li>
|
||||
{% if not user.profile.login_clipper %}<li><a href="{% url "django.contrib.auth.views.password_change" %}">Changer mon mot de passe</a></li>{% endif %}
|
||||
<li><a href="{% url "gestioncof.views.logout" %}">Se déconnecter</a></li>
|
||||
<li><a href="{% url "gestioncof.views.logout" %}">Se déconnecter</a></li>
|
||||
</ul>
|
||||
|
||||
{% if user.profile.is_buro %}
|
||||
<h3>Administration</h3>
|
||||
<ul>
|
||||
<li><a href="{% url "admin:index" %}">Administration générale</a></li>
|
||||
<li><a href="{% url "petits-cours-demandes-list" %}">Demandes de petits cours</a></li>
|
||||
<li><a href="{% url "gestioncof.views.registration" %}">Inscription d'un nouveau membre</a></li>
|
||||
<br>
|
||||
<li><a href="{% url "admin:index" %}">Administration générale</a></li>
|
||||
<li><a href="{% url "petits-cours-demandes-list" %}">Demandes de petits cours</a></li>
|
||||
<li><a href="{% url "gestioncof.views.registration" %}">Inscription d'un nouveau membre</a></li>
|
||||
<br>
|
||||
{% for event in events %}
|
||||
<li><a href="{% url "gestioncof.views.event_status" event.id %}">Événement : {{ event.title }}</a></li>
|
||||
{% endfor %}
|
||||
{% for survey in surveys %}
|
||||
<li><a href="{% url "gestioncof.views.survey_status" survey.id %}">Sondage : {{ survey.title }}</a></li>
|
||||
{% endfor %}
|
||||
<br>
|
||||
<li><a href="{% url "gestioncof.views.utile_cof" %}">Liens utiles du COF</a></li>
|
||||
<li><a href="{% url "gestioncof.views.utile_bda" %}">Liens utiles BdA</a></li>
|
||||
<br>
|
||||
<li><a href="{% url "gestioncof.views.utile_cof" %}">Liens utiles du COF</a></li>
|
||||
<li><a href="{% url "gestioncof.views.utile_bda" %}">Liens utiles BdA</a></li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
<h3>Pour tout problème : cof@ens.fr.</h3>
|
||||
<h3>Pour tout problème : cof@ens.fr.</h3>
|
||||
{% endblock %}
|
||||
|
|
|
@ -10,23 +10,14 @@
|
|||
<li><a href="{% url 'gestioncof.views.liste_bdadiff' %}">BdA diffusion</a></li>
|
||||
<li><a href="{% url 'gestioncof.views.liste_bdarevente' %}">BdA revente</a></li>
|
||||
</ul>
|
||||
<h3>Premier tirage</h3>
|
||||
<h3>Tirages</h3>
|
||||
{% for tirage in tirages %}
|
||||
<h4>{{ tirage.title }}</h4>
|
||||
<ul>
|
||||
<li><a href="{% url 'bda.views.etat_places' %}">Etat des voeux</a></li>
|
||||
<li><a href="{% url 'bda-liste-spectacles' %}">Mailing list par spectacle</a></li>
|
||||
<li><a href="{% url 'bda.views.unpaid' %}">Mailing list des impayés</a></li>
|
||||
<li><a href="{% url 'bda-liste-spectacles-ics' %}">Calendrier des spectacles (.ics)</a></li>
|
||||
<li><a href="{% url 'bda.views.etat_places' tirage.id %}">Etat des voeux</a></li>
|
||||
<li><a href="{% url 'bda-liste-spectacles' tirage.id %}">Mailing list par spectacle</a></li>
|
||||
<li><a href="{% url 'bda.views.unpaid' tirage.id %}">Mailing list des impayés</a></li>
|
||||
<li><a href="{% url 'bda-liste-spectacles-ics' tirage.id %}">Calendrier des spectacles (.ics)</a></li>
|
||||
</ul>
|
||||
<h3>Deuxième tirage</h3>
|
||||
<ul>
|
||||
<li><a href="{% url 'bda2.views.etat_places' %}">Etat des voeux</a></li>
|
||||
<li><a href="{% url 'bda2-liste-spectacles' %}">Mailing list par spectacle</a></li>
|
||||
<li><a href="{% url 'bda2.views.unpaid' %}">Mailing list des impayés</a></li>
|
||||
</ul>
|
||||
<h3>Troisième tirage</h3>
|
||||
<ul>
|
||||
<li><a href="{% url 'bda3.views.etat_places' %}">Etat des voeux</a></li>
|
||||
<li><a href="{% url 'bda3-liste-spectacles' %}">Mailing list par spectacle</a></li>
|
||||
<li><a href="{% url 'bda3.views.unpaid' %}">Mailing list des impayés</a></li>
|
||||
</ul>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -20,14 +20,17 @@ from gestioncof.forms import UserProfileForm, EventStatusFilterForm, \
|
|||
SurveyForm, SurveyStatusFilterForm, RegistrationUserForm, \
|
||||
RegistrationProfileForm, AdminEventForm, EventForm
|
||||
|
||||
from bda.models import Tirage
|
||||
|
||||
import re
|
||||
|
||||
@login_required
|
||||
def home(request):
|
||||
data = {"surveys": Survey.objects.filter(old = False).all(),
|
||||
"events": Event.objects.filter(old = False).all(),
|
||||
"open_surveys": Survey.objects.filter(survey_open = True, old = False).all(),
|
||||
"open_events": Event.objects.filter(registration_open = True, old = False).all()}
|
||||
data = {"surveys": Survey.objects.filter(old=False).all(),
|
||||
"events": Event.objects.filter(old=False).all(),
|
||||
"open_surveys": Survey.objects.filter(survey_open=True, old=False).all(),
|
||||
"open_events": Event.objects.filter(registration_open=True, old=False).all(),
|
||||
"open_tirages": Tirage.objects.filter(active=True).all()}
|
||||
return render(request, "home.html", data)
|
||||
|
||||
def login(request):
|
||||
|
@ -496,7 +499,8 @@ def utile_cof(request):
|
|||
|
||||
@buro_required
|
||||
def utile_bda(request):
|
||||
return render(request, "utile_bda.html", {})
|
||||
tirages = Tirage.objects.all()
|
||||
return render(request, "utile_bda.html", {'tirages': tirages})
|
||||
|
||||
@buro_required
|
||||
def liste_bdadiff(request):
|
||||
|
|
Loading…
Reference in a new issue