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:
Basile Clement 2016-06-07 23:38:24 +02:00
commit fb16276ee3
56 changed files with 339 additions and 3277 deletions

View file

@ -5,7 +5,9 @@ from django.contrib.contenttypes.models import ContentType
from django.contrib import admin from django.contrib import admin
from django.db.models import Sum, Count 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): class ChoixSpectacleInline(admin.TabularInline):
model = ChoixSpectacle model = ChoixSpectacle
@ -37,62 +39,38 @@ class ParticipantAdmin(admin.ModelAdmin):
actions_on_bottom = True actions_on_bottom = True
list_per_page = 400 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): def send_attribs(self, request, queryset):
for member in queryset.all(): for member in queryset.all():
attribs = member.attributions.all() attribs = member.attributions.all()
if len(attribs) == 0: 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 : pour les spectacles suivants :
%s %s
*Paiement* *Paiement*
L'intégralité de ces places de spectacles est à régler à partir du lundi L'intégralité de ces places de spectacles est à régler dès maintenant et AVANT
6 octobre et AVANT le vendredi 10 octobre, au bureau du COF pendant les le %s, au bureau du COF pendant les heures de permanences (du lundi au vendredi
heures de permanences (du lundi au vendredi entre 12h et 14h, et entre 18h entre 12h et 14h, et entre 18h et 20h). Des facilités de paiement sont bien
et 20h). Des facilités de paiement sont bien évidemment possibles : nous évidemment possibles : nous pouvons ne pas encaisser le chèque immédiatement, ou
pouvons ne pas encaisser le chèque immédiatement, ou bien découper votre bien découper votre paiement en deux fois. Pour ceux qui ne pourraient pas venir
paiement en deux fois. Pour ceux qui ne pourraient pas venir payer au bureau, payer au bureau, merci de nous contacter par mail.
merci de nous contacter par mail.
*Mode de retrait des places* *Mode de retrait des places*
Au moment du paiement, certaines places vous seront remises directement, d'autres Au moment du paiement, certaines places vous seront remises directement, d'autres
@ -109,13 +87,13 @@ prochainement disponible, directement sur votre compte GestioCOF.
En vous souhaitant de très beaux spectacles tout au long de l'année, En vous souhaitant de très beaux spectacles tout au long de l'année,
-- --
Le Bureau des Arts Le Bureau des Arts
(Jean, Antoine, Élodie, Marion et Louise)
""" """
attribs_text = "" attribs_text = ""
name = member.user.get_full_name() name = member.user.get_full_name()
for attrib in attribs: for attrib in attribs:
attribs_text += u"- 1 place pour %s\n" % attrib attribs_text += u"- 1 place pour %s\n" % attrib
mail = mail % (name, attribs_text) 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, send_mail ("Résultats du tirage au sort", mail,
"bda@ens.fr", [member.user.email], "bda@ens.fr", [member.user.email],
fail_silently = True) fail_silently = True)
@ -155,3 +133,4 @@ admin.site.register(Salle)
admin.site.register(Participant, ParticipantAdmin) admin.site.register(Participant, ParticipantAdmin)
admin.site.register(Attribution, AttributionAdmin) admin.site.register(Attribution, AttributionAdmin)
admin.site.register(ChoixSpectacle, ChoixSpectacleAdmin) admin.site.register(ChoixSpectacle, ChoixSpectacleAdmin)
admin.site.register(Tirage)

View file

@ -59,12 +59,6 @@ class Algorithm(object):
self.origranks[member][show], self.origranks[member][show],
self.choices[member][show].double)) 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): def __call__(self, seed):
random.seed(seed) random.seed(seed)
results = [] results = []
@ -102,3 +96,4 @@ class Algorithm(object):
self.IncrementRanks(member, i) self.IncrementRanks(member, i)
results.append((show,winners,losers)) results.append((show,winners,losers))
return results return results

View file

@ -1,7 +1,7 @@
# coding: utf-8 # coding: utf-8
from django import forms 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 from bda.models import Spectacle, Participant, ChoixSpectacle, Attribution
class BaseBdaFormSet(BaseInlineFormSet): class BaseBdaFormSet(BaseInlineFormSet):
@ -19,19 +19,21 @@ class BaseBdaFormSet(BaseInlineFormSet):
spectacle = form.cleaned_data['spectacle'] spectacle = form.cleaned_data['spectacle']
delete = form.cleaned_data['DELETE'] delete = form.cleaned_data['DELETE']
if not delete and spectacle in spectacles: 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) spectacles.append(spectacle)
class TokenForm(forms.Form): class TokenForm(forms.Form):
token = forms.CharField(widget = forms.widgets.Textarea()) token = forms.CharField(widget=forms.widgets.Textarea())
class SpectacleModelChoiceField(forms.ModelChoiceField): class SpectacleModelChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj): 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): class ResellForm(forms.Form):
count = forms.ChoiceField(choices = (("1","1"),("2","2"),)) 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): def __init__(self, participant, *args, **kwargs):
super(ResellForm, self).__init__(*args, **kwargs) super(ResellForm, self).__init__(*args, **kwargs)

View 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,
),
]

View file

@ -7,22 +7,36 @@ from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.db.models.signals import post_save from django.db.models.signals import post_save
class Salle (models.Model): class Tirage(models.Model):
name = models.CharField ("Nom", max_length = 300) title = models.CharField("Titre", max_length=300)
address = models.TextField ("Adresse") 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): def __unicode__ (self):
return self.name return self.name
class Spectacle (models.Model): class Spectacle(models.Model):
title = models.CharField ("Titre", max_length = 300) title = models.CharField("Titre", max_length=300)
date = models.DateTimeField ("Date & heure") date = models.DateTimeField("Date & heure")
location = models.ForeignKey(Salle) location = models.ForeignKey(Salle)
description = models.TextField ("Description", blank = True) 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) price = models.FloatField("Prix d'une place")
slots = models.IntegerField ("Places") slots = models.IntegerField("Places")
priority = models.IntegerField ("Priorité", default = 1000) priority = models.IntegerField("Priorité", default=1000)
tirage = models.ForeignKey(Tirage)
class Meta: class Meta:
verbose_name = "Spectacle" verbose_name = "Spectacle"
@ -38,7 +52,8 @@ class Spectacle (models.Model):
return self.date.strftime('%d %b %Y %H:%M') return self.date.strftime('%d %b %Y %H:%M')
def __unicode__ (self): 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 = ( PAYMENT_TYPES = (
("cash",u"Cash"), ("cash",u"Cash"),
@ -47,12 +62,18 @@ PAYMENT_TYPES = (
("autre",u"Autre"), ("autre",u"Autre"),
) )
class Participant (models.Model): class Participant(models.Model):
user = models.OneToOneField(User) user = models.ForeignKey(User)
choices = models.ManyToManyField(Spectacle, through = "ChoixSpectacle", related_name = "chosen_by") choices = models.ManyToManyField(Spectacle,
attributions = models.ManyToManyField(Spectacle, through = "Attribution", related_name = "attributed_to") through="ChoixSpectacle",
paid = models.BooleanField (u"A payé", default = False) related_name="chosen_by")
paymenttype = models.CharField(u"Moyen de paiement", max_length = 6, choices = PAYMENT_TYPES, blank = True) 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): def __unicode__ (self):
return u"%s" % (self.user) return u"%s" % (self.user)
@ -63,11 +84,12 @@ DOUBLE_CHOICES = (
("double", "2 places sinon rien"), ("double", "2 places sinon rien"),
) )
class ChoixSpectacle (models.Model): class ChoixSpectacle(models.Model):
participant = models.ForeignKey(Participant) participant = models.ForeignKey(Participant)
spectacle = models.ForeignKey(Spectacle, related_name = "participants") spectacle = models.ForeignKey(Spectacle, related_name="participants")
priority = models.PositiveIntegerField("Priorité") 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): def get_double(self):
return self.double_choice != "1" return self.double_choice != "1"
@ -83,10 +105,11 @@ class ChoixSpectacle (models.Model):
verbose_name = "voeu" verbose_name = "voeu"
verbose_name_plural = "voeux" verbose_name_plural = "voeux"
class Attribution (models.Model): class Attribution(models.Model):
participant = models.ForeignKey(Participant) participant = models.ForeignKey(Participant)
spectacle = models.ForeignKey(Spectacle, related_name = "attribues") spectacle = models.ForeignKey(Spectacle, related_name="attribues")
given = models.BooleanField(u"Donnée", default = False) given = models.BooleanField(u"Donnée", default=False)
def __unicode__ (self): def __unicode__ (self):
return u"%s -- %s" % (self.participant, self.spectacle) return u"%s -- %s" % (self.participant, self.spectacle)

View file

@ -3,7 +3,8 @@
{% block extracontent %} {% block extracontent %}
<h1>Attribution (détails)</h1> <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> <h2>Placés : {{ total_slots }} ; Déçus : {{ total_losers }}</h2>
<table> <table>

View file

@ -8,7 +8,8 @@
{% block realcontent %} {% block realcontent %}
<h1>Attribution</h1> <h1>Attribution</h1>
<h2>Token : {{ token }}</h2> <h2>Token :</h2>
<pre>{{ token }}</pre>
<h2>Placés : {{ total_slots }} ; Déçus : {{ total_losers }}</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 %} {% 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> <h2>Temps de calcul : {{ duration|floatformat }}s</h2>

View file

@ -98,7 +98,7 @@ var django = {
{% if stateerror %} {% if stateerror %}
<p class="error">Impossible d'enregistrer vos modifications: vous avez apporté d'autres modifications entre temps</p> <p class="error">Impossible d'enregistrer vos modifications: vous avez apporté d'autres modifications entre temps</p>
{% endif %} {% 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 %} {% csrf_token %}
{% include "inscription-formset.html" %} {% include "inscription-formset.html" %}
<input type="button" class="btn-addmore" value="Ajouter un autre v&oelig;u" id="add_more"> <input type="button" class="btn-addmore" value="Ajouter un autre v&oelig;u" id="add_more">

View file

@ -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'>&nbsp;</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&oelig;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&oelig;u est ordonnée (du plus important au moins important), pour ajuster la priorité vous pouvez déplacer chaque v&oelig;u<br />
</p>
</form>
{% endblock %}

View file

@ -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'>&nbsp;</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&oelig;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&oelig;u est ordonnée (du plus important au moins important), pour ajuster la priorité vous pouvez déplacer chaque v&oelig;u<br />
</p>
</form>
{% endblock %}

View file

@ -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'>&nbsp;</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&oelig;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&oelig;u est ordonnée (du plus important au moins important), pour ajuster la priorité vous pouvez déplacer chaque v&oelig;u<br />
</p>
</form>
{% endblock %}

View file

@ -12,7 +12,7 @@
{% endfor %} {% endfor %}
</ol> </ol>
<h4>Total à payer : {{ total|floatformat }}€</h4> <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 %} {% else %}
<h3>Vous n'avez aucune place :(</h3> <h3>Vous n'avez aucune place :(</h3>
{% endif %} {% endif %}

View file

@ -3,9 +3,9 @@
{% block realcontent %} {% block realcontent %}
<h1><strong>Spectacles</strong></h1> <h1><strong>Spectacles</strong></h1>
<ul> <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 %} {% 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 %} {% endfor %}
</ul> </ul>
{% endblock %} {% endblock %}

View file

@ -6,25 +6,39 @@ from django.contrib.auth.decorators import login_required
from django.db import models from django.db import models
from django.http import Http404 from django.http import Http404
from django.core import serializers from django.core import serializers
from django.forms.models import inlineformset_factory
import hashlib import hashlib
from django.core.mail import send_mail 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 import time
from gestioncof.decorators import cof_required, buro_required from gestioncof.decorators import cof_required, buro_required
from gestioncof.shared import send_custom_mail 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.algorithm import Algorithm
from bda.forms import BaseBdaFormSet, TokenForm, ResellForm from bda.forms import BaseBdaFormSet, TokenForm, ResellForm
@cof_required @cof_required
def etat_places(request): def etat_places(request, tirage_id):
spectacles1 = ChoixSpectacle.objects.filter(double_choice = "1").all().values('spectacle','spectacle__title').annotate(total = models.Count('spectacle')) tirage = get_object_or_404(Tirage, id=tirage_id)
spectacles2 = ChoixSpectacle.objects.exclude(double_choice = "1").all().values('spectacle','spectacle__title').annotate(total = models.Count('spectacle')) spectacles1 = ChoixSpectacle.objects \
spectacles = Spectacle.objects.all() .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 = {} spectacles_dict = {}
total = 0 total = 0
for spectacle in spectacles: for spectacle in spectacles:
@ -33,13 +47,18 @@ def etat_places(request):
spectacles_dict[spectacle.id] = spectacle spectacles_dict[spectacle.id] = spectacle
for spectacle in spectacles1: for spectacle in spectacles1:
spectacles_dict[spectacle["spectacle"]].total += spectacle["total"] 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"] total += spectacle["total"]
for spectacle in spectacles2: for spectacle in spectacles2:
spectacles_dict[spectacle["spectacle"]].total += 2*spectacle["total"] 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"] 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): def _hash_queryset(queryset):
data = serializers.serialize("json", queryset) data = serializers.serialize("json", queryset)
@ -48,9 +67,12 @@ def _hash_queryset(queryset):
return hasher.hexdigest() return hasher.hexdigest()
@cof_required @cof_required
def places(request): def places(request, tirage_id):
participant, created = Participant.objects.get_or_create(user = request.user) tirage = get_object_or_404(Tirage, id=tirage_id)
places = participant.attribution_set.order_by("spectacle__date", "spectacle").all() 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]) total = sum([place.spectacle.price for place in places])
filtered_places = [] filtered_places = []
places_dict = {} places_dict = {}
@ -73,13 +95,17 @@ def places(request):
return render(request, "resume_places.html", return render(request, "resume_places.html",
{"participant": participant, {"participant": participant,
"places": filtered_places, "places": filtered_places,
"tirage": tirage,
"total": total, "total": total,
"warning": warning}) "warning": warning})
@cof_required @cof_required
def places_ics(request): def places_ics(request, tirage_id):
participant, created = Participant.objects.get_or_create(user = request.user) tirage = get_object_or_404(Tirage, id=tirage_id)
places = participant.attribution_set.order_by("spectacle__date", "spectacle").all() participant, created = Participant.objects.get_or_create(
user=request.user, tirage=tirage)
places = participant.attribution_set.order_by(
"spectacle__date", "spectacle").all()
filtered_places = [] filtered_places = []
places_dict = {} places_dict = {}
spectacles = [] spectacles = []
@ -98,45 +124,75 @@ def places_ics(request):
"places": filtered_places}, content_type="text/calendar") "places": filtered_places}, content_type="text/calendar")
@cof_required @cof_required
def inscription(request): def inscription(request, tirage_id):
if datetime.now() > datetime(2015, 10, 4, 12, 00): tirage = get_object_or_404(Tirage, id=tirage_id)
participant, created = Participant.objects.get_or_create(user = request.user) 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() 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}) return render(request, "resume_inscription.html",
BdaFormSet = inlineformset_factory(Participant, ChoixSpectacle, fields = ("spectacle","double_choice","priority",), formset = BaseBdaFormSet) { "error_title": "C'est fini !",
participant, created = Participant.objects.get_or_create(user = request.user) "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 success = False
stateerror = False stateerror = False
if request.method == "POST": if request.method == "POST":
dbstate = _hash_queryset(participant.choixspectacle_set.all()) dbstate = _hash_queryset(participant.choixspectacle_set.all())
if "dbstate" in request.POST and dbstate != request.POST["dbstate"]: if "dbstate" in request.POST and dbstate != request.POST["dbstate"]:
stateerror = True stateerror = True
formset = BdaFormSet(instance = participant) formset = BdaFormSet(instance=participant)
else: else:
formset = BdaFormSet(request.POST, instance = participant) formset = BdaFormSet(request.POST, instance=participant)
if formset.is_valid(): if formset.is_valid():
#ChoixSpectacle.objects.filter(participant = participant).delete()
formset.save() formset.save()
success = True success = True
formset = BdaFormSet(instance = participant) formset = BdaFormSet(instance=participant)
else: else:
formset = BdaFormSet(instance = participant) formset = BdaFormSet(instance=participant)
dbstate = _hash_queryset(participant.choixspectacle_set.all()) dbstate = _hash_queryset(participant.choixspectacle_set.all())
total_price = 0 total_price = 0
for choice in participant.choixspectacle_set.all(): for choice in participant.choixspectacle_set.all():
total_price += choice.spectacle.price total_price += choice.spectacle.price
if choice.double: 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) form = TokenForm(request.POST)
if not form.is_valid(): if not form.is_valid():
return tirage(request) return tirage(request)
tirage_elt.token = form.cleaned_data['token']
tirage_elt.save()
start = time.time() start = time.time()
data = {} data = {}
shows = Spectacle.objects.select_related().all() shows = tirage_elt.spectacle_set.select_related().all()
members = Participant.objects.all() members = tirage_elt.spectacle_set.all()
choices = ChoixSpectacle.objects.order_by('participant', 'priority').select_related().all() choices = ChoixSpectacle.objects.filter(spectacle__tirage=tirage_elt).order_by(
'participant', 'priority').select_related().all()
algo = Algorithm(shows, members, choices) algo = Algorithm(shows, members, choices)
results = algo(form.cleaned_data["token"]) results = algo(form.cleaned_data["token"])
total_slots = 0 total_slots = 0
@ -178,23 +234,26 @@ def do_tirage(request):
members2[member].append(show) members2[member].append(show)
member.total += show.price member.total += show.price
members2 = members2.items() members2 = members2.items()
data["members2"] = sorted(members2, key = lambda m: m[0].user.last_name) data["members2"] = sorted(members2, key=lambda m: m[0].user.last_name)
if False and request.user.username in ["seguin", "harazi","fromherz", "ccadiou"]: # À partir d'ici, le tirage devient effectif
# FIXME: Établir les conditions de validations (formulaire ?)
# cf. issue #32
if False:
Attribution.objects.all().delete() Attribution.objects.all().delete()
for (show, members, _) in results: for (show, members, _) in results:
for (member, _, _, _) in members: for (member, _, _, _) in members:
attrib = Attribution(spectacle = show, participant = member) attrib = Attribution(spectacle=show, participant=member)
attrib.save() attrib.save()
return render(request, "bda-attrib-extra.html", data) return render(request, "bda-attrib-extra.html", data)
else: else:
return render(request, "bda-attrib.html", data) return render(request, "bda-attrib.html", data)
@login_required @buro_required
def tirage(request): def tirage(request, tirage_id):
if request.POST: if request.POST:
form = TokenForm(request.POST) form = TokenForm(request.POST)
if form.is_valid(): if form.is_valid():
return do_tirage(request) return do_tirage(request, tirage_id)
else: else:
form = TokenForm() form = TokenForm()
return render(request, "bda-token.html", {"form": form}) 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. Je souhaite revendre %s pour %s le %s (%s) à %.02f.
Contactez moi par email si vous êtes intéressé·e·s ! 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, send_mail("%s" % spectacle, mail,
request.user.email, ["bda-revente@lists.ens.fr"], request.user.email, ["bda-revente@lists.ens.fr"],
fail_silently = False) fail_silently = False)
return render(request, "bda-success.html", {"show": spectacle, "places": places}) return render(request, "bda-success.html", {"show": spectacle, "places": places})
@login_required @login_required
def revente(request): def revente(request, tirage_id):
participant, created = Participant.objects.get_or_create(user = request.user) 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: if not participant.paid:
return render(request, "bda-notpaid.html", {}) return render(request, "bda-notpaid.html", {})
if request.POST: if request.POST:
@ -237,21 +300,40 @@ def revente(request):
return do_resell(request, form) return do_resell(request, form)
else: else:
form = ResellForm(participant) form = ResellForm(participant)
return render(request, "bda-revente.html", {"form": form}) return render(request, "bda-revente.html", {"form": form, 'tirage': tirage})
@buro_required @buro_required
def spectacle(request, spectacle_id): def spectacle(request, tirage_id, spectacle_id):
spectacle = get_object_or_404(Spectacle, 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}) return render(request, "bda-emails.html", {"spectacle": spectacle})
@buro_required
def unpaid(request): class SpectacleListView(ListView):
return render(request, "bda-unpaid.html", {"unpaid": Participant.objects.filter(paid = False).all()}) 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 @buro_required
def liste_spectacles_ics(request): def unpaid(request, tirage_id):
spectacles = Spectacle.objects.order_by("date").all() 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, tirage_id):
tirage = get_object_or_404(Tirage, id=tirage_id)
spectacles = tirage.spectacle_set.order_by("date").all()
for spectacle in spectacles: for spectacle in spectacles:
spectacle.dtend = spectacle.date + timedelta(seconds=7200) spectacle.dtend = spectacle.date + timedelta(seconds=7200)
return render(request, "liste_spectacles.ics", return render(request, "liste_spectacles.ics",
{"spectacles": spectacles}, content_type="text/calendar") {"spectacles": spectacles, "tirage": tirage},
content_type="text/calendar")

View file

View file

@ -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)

View file

@ -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

View file

@ -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...'})

View file

@ -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~

View file

@ -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()

View file

@ -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')]),
),
]

View file

@ -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)

View file

@ -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;}

View file

@ -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 %}

View file

@ -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 }} &mdash; 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 }} &mdash; rang {{ rank }})</span></li>
{% endfor %}
</ul>
{% endif %}
</div>
{% endfor %}
{% block extracontent %}
{% endblock %}
{% endblock %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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'>&nbsp;</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&oelig;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&oelig;u est ordonnée (du plus important au moins important), pour ajuster la priorité vous pouvez déplacer chaque v&oelig;u<br />
</p>
</form>
{% endblock %}

View file

@ -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>

View file

@ -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 %}

View file

@ -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)

View file

@ -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()})

View file

View file

@ -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)

View file

@ -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

View file

@ -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...'})

View file

@ -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~

View file

@ -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()

View file

@ -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')]),
),
]

View file

@ -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)

View file

@ -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;}

View file

@ -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 %}

View file

@ -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)

View file

@ -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()})

View file

@ -41,8 +41,6 @@ INSTALLED_APPS = (
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.admindocs', 'django.contrib.admindocs',
'bda', 'bda',
'bda2',
'bda3',
'autocomplete_light', 'autocomplete_light',
'captcha', 'captcha',
'django_cas_ng', 'django_cas_ng',

View file

@ -6,11 +6,9 @@ autocomplete_light.autodiscover()
from django.contrib import admin from django.contrib import admin
admin.autodiscover() admin.autodiscover()
from django.views.generic.list import ListView
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView
from bda.models import Spectacle from bda.models import Spectacle
from bda2.models import Spectacle as Spectacle2 from bda.views import SpectacleListView
from bda3.models import Spectacle as Spectacle3
from gestioncof.petits_cours_views import DemandeListView from gestioncof.petits_cours_views import DemandeListView
urlpatterns = patterns('', 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+)$', '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+)/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'^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'^bda/inscription/(?P<tirage_id>\d+)$', 'bda.views.inscription', name = 'bda-tirage-inscription'),
url(r'^bda2/inscription$', 'bda2.views.inscription', name = 'bda2-tirage-inscription'), url(r'^bda/places/(?P<tirage_id>\d+)$', 'bda.views.places', name = "bda-places-attribuees"),
url(r'^bda3/inscription$', 'bda3.views.inscription', name = 'bda3-tirage-inscription'), url(r'^bda/places/(?P<tirage_id>\d+)/places_bda.ics$', 'bda.views.places_ics', name = "bda-places-attribuees-ics"),
url(r'^bda/places$', 'bda.views.places', name = "bda-places-attribuees"), url(r'^bda/revente/(?P<tirage_id>\d+)$', 'bda.views.revente', name = 'bda-revente'),
url(r'^bda/places/places_bda.ics$', 'bda.views.places_ics', name = "bda-places-attribuees-ics"), url(r'^bda/etat-places/(?P<tirage_id>\d+)$', 'bda.views.etat_places', name = 'bda-etat-places'),
url(r'^bda2/places$', 'bda2.views.places', name = "bda2-places-attribuees"), url(r'^bda/tirage/(?P<tirage_id>\d+)$', 'bda.views.tirage'),
url(r'^bda3/places$', 'bda3.views.places', name = "bda3-places-attribuees"), url(r'^bda/spectacles/(?P<tirage_id>\d+)$', SpectacleListView.as_view() , name ="bda-liste-spectacles"),
url(r'^bda/revente$', 'bda.views.revente', name = 'bda-revente'), url(r'^bda/spectacles/(?P<tirage_id>\d+)/(?P<spectacle_id>\d+)$', "bda.views.spectacle", name = "bda-spectacle"),
url(r'^bda2/revente$', 'bda2.views.revente', name = 'bda2-revente'), url(r'^bda/spectacles-ics/(?P<tirage_id>\d+)$', 'bda.views.liste_spectacles_ics', name ="bda-liste-spectacles-ics"),
url(r'^bda3/revente$', 'bda3.views.revente', name = 'bda3-revente'), url(r'^bda/spectacles/unpaid/(?P<tirage_id>\d+)$', "bda.views.unpaid", name = "bda-unpaid"),
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'^survey/(?P<survey_id>\d+)$', 'gestioncof.views.survey'), url(r'^survey/(?P<survey_id>\d+)$', 'gestioncof.views.survey'),
url(r'^event/(?P<event_id>\d+)$', 'gestioncof.views.event'), url(r'^event/(?P<event_id>\d+)$', 'gestioncof.views.event'),
url(r'^survey/(?P<survey_id>\d+)/status$', 'gestioncof.views.survey_status'), url(r'^survey/(?P<survey_id>\d+)/status$', 'gestioncof.views.survey_status'),

View file

@ -24,29 +24,19 @@
{% if user.profile.is_cof %} {% if user.profile.is_cof %}
<h3>BdA</h3> {% if open_tirages %}
<h3>Tirages du BdA</h3>
{% for tirage in open_tirages %}
<ul> <ul>
<!-- <li><a href="{% url "bda-tirage-inscription" %}">Inscription au premier tirage au sort du BdA</a></li> {{ tirage.title }}
<li><a href="{% url "bda-etat-places" %}">État des demandes</a>--> <li><a href="{% url "bda-tirage-inscription" tirage.id %}">Inscription</a></li>
<li><a href="{% url "bda-places-attribuees" %}">Mes places du premier tirage</a></li> <li><a href="{% url "bda-etat-places" tirage.id %}">État des demandes</a>
<li><a href="{% url "bda-revente" %}">Revendre une place du premier tirage</a></li> <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> <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> </ul>
{% endfor %}
{% endif %}
{% endif %} {% endif %}
<h3>Divers</h3> <h3>Divers</h3>

View file

@ -10,23 +10,14 @@
<li><a href="{% url 'gestioncof.views.liste_bdadiff' %}">BdA diffusion</a></li> <li><a href="{% url 'gestioncof.views.liste_bdadiff' %}">BdA diffusion</a></li>
<li><a href="{% url 'gestioncof.views.liste_bdarevente' %}">BdA revente</a></li> <li><a href="{% url 'gestioncof.views.liste_bdarevente' %}">BdA revente</a></li>
</ul> </ul>
<h3>Premier tirage</h3> <h3>Tirages</h3>
{% for tirage in tirages %}
<h4>{{ tirage.title }}</h4>
<ul> <ul>
<li><a href="{% url 'bda.views.etat_places' %}">Etat des voeux</a></li> <li><a href="{% url 'bda.views.etat_places' tirage.id %}">Etat des voeux</a></li>
<li><a href="{% url 'bda-liste-spectacles' %}">Mailing list par spectacle</a></li> <li><a href="{% url 'bda-liste-spectacles' tirage.id %}">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.views.unpaid' tirage.id %}">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-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> </ul>
{% endfor %}
{% endblock %} {% endblock %}

View file

@ -20,14 +20,17 @@ from gestioncof.forms import UserProfileForm, EventStatusFilterForm, \
SurveyForm, SurveyStatusFilterForm, RegistrationUserForm, \ SurveyForm, SurveyStatusFilterForm, RegistrationUserForm, \
RegistrationProfileForm, AdminEventForm, EventForm RegistrationProfileForm, AdminEventForm, EventForm
from bda.models import Tirage
import re import re
@login_required @login_required
def home(request): def home(request):
data = {"surveys": Survey.objects.filter(old = False).all(), data = {"surveys": Survey.objects.filter(old=False).all(),
"events": Event.objects.filter(old = False).all(), "events": Event.objects.filter(old=False).all(),
"open_surveys": Survey.objects.filter(survey_open = True, 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_events": Event.objects.filter(registration_open=True, old=False).all(),
"open_tirages": Tirage.objects.filter(active=True).all()}
return render(request, "home.html", data) return render(request, "home.html", data)
def login(request): def login(request):
@ -496,7 +499,8 @@ def utile_cof(request):
@buro_required @buro_required
def utile_bda(request): def utile_bda(request):
return render(request, "utile_bda.html", {}) tirages = Tirage.objects.all()
return render(request, "utile_bda.html", {'tirages': tirages})
@buro_required @buro_required
def liste_bdadiff(request): def liste_bdadiff(request):