Grod commit dégueux avec une tonne de trucs. Berk.
This commit is contained in:
parent
d5b3d3f958
commit
64b8ee4133
17 changed files with 536 additions and 209 deletions
0
apache/django.wsgi
Executable file → Normal file
0
apache/django.wsgi
Executable file → Normal file
68
bda/admin.py
68
bda/admin.py
|
@ -4,7 +4,7 @@ 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
|
||||
from django.db.models import Sum, Count
|
||||
from bda.models import Spectacle, Salle, Participant, ChoixSpectacle, Attribution
|
||||
|
||||
class ChoixSpectacleInline(admin.TabularInline):
|
||||
|
@ -17,13 +17,18 @@ class AttributionInline(admin.TabularInline):
|
|||
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 len(obj.attribution_set.all())
|
||||
return obj.nb_places
|
||||
nb_places.admin_order_field = "nb_places"
|
||||
nb_places.short_description = "Nombre de places"
|
||||
def total(self, obj):
|
||||
tot = obj.attributions.aggregate(total = Sum('price'))['total']
|
||||
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",)
|
||||
|
@ -56,7 +61,7 @@ Voici tes choix de spectacles tels que notre système les a enregistrés :\n\n""
|
|||
Artistiquement,
|
||||
Le BdA"""
|
||||
send_mail ("Choix de spectacles (BdA du COF)", mail,
|
||||
"bda@clipper.ens.fr", [member.user.email],
|
||||
"bda@ens.fr", [member.user.email],
|
||||
fail_silently = True)
|
||||
count = len(queryset.all())
|
||||
if count == 1:
|
||||
|
@ -81,35 +86,48 @@ pour les spectacles suivants :
|
|||
%s
|
||||
|
||||
*Paiement*
|
||||
Ces spectacles sont à régler avant le vendredi 19 Octobre, 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).
|
||||
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.
|
||||
|
||||
*Mode de retrait des places*
|
||||
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.
|
||||
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 et la Salle
|
||||
Pleyel, 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 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 Comédie Française, le théâtre de la Ville,
|
||||
le théâtre de Chaillot et l'IRCAM, les places seront distribuées dans vos
|
||||
casiers environ une semaine avant la représentation (un mail vous en
|
||||
avertira).
|
||||
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).
|
||||
|
||||
Culturellement vôtre,
|
||||
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.
|
||||
|
||||
En vous souhaitant de très beaux spectacles tout au long de l'année,
|
||||
--
|
||||
Le BdA"""
|
||||
Le Bureau des Arts
|
||||
(Chloé, Emilie, Jaime, Maxime, Olivier)
|
||||
"""
|
||||
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 ("Places de spectacle (BdA du COF)", mail,
|
||||
"bda@clipper.ens.fr", [member.user.email],
|
||||
send_mail ("Résultats du tirage au sort", mail,
|
||||
"bda@ens.fr", [member.user.email],
|
||||
fail_silently = True)
|
||||
count = len(queryset.all())
|
||||
if count == 1:
|
||||
|
@ -136,7 +154,13 @@ class ChoixSpectacleAdmin(admin.ModelAdmin):
|
|||
list_filter = ("double", "autoquit")
|
||||
search_fields = ('participant__user__username', 'participant__user__first_name', 'participant__user__last_name')
|
||||
|
||||
admin.site.register(Spectacle)
|
||||
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)
|
||||
|
|
|
@ -29,25 +29,24 @@ class Algorithm(object):
|
|||
self.ranks = {}
|
||||
self.origranks = {}
|
||||
self.choices = {}
|
||||
next_rank = {}
|
||||
member_shows = {}
|
||||
for member in members:
|
||||
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)
|
||||
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]:
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# 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 _
|
||||
|
@ -29,11 +31,14 @@ 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"),
|
||||
|
|
106
bda/views.py
106
bda/views.py
|
@ -1,17 +1,22 @@
|
|||
# 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 gestioncof.shared import send_custom_mail
|
||||
from bda.models import Spectacle, Participant, ChoixSpectacle, Attribution
|
||||
from bda.algorithm import Algorithm
|
||||
|
||||
|
@ -42,47 +47,93 @@ 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 False and time.time() > 1349474400:
|
||||
return render(request, "error.html", {"error_title": "C'est fini !", "error_description": u"Tirage au sort le 6 octobre dans la soirée "})
|
||||
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})
|
||||
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":
|
||||
formset = BdaFormSet(request.POST, instance = participant)
|
||||
if formset.is_valid():
|
||||
#ChoixSpectacle.objects.filter(participant = participant).delete()
|
||||
formset.save()
|
||||
success = True
|
||||
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-bda.html", {"formset": formset, "success": success, "total_price": total_price})
|
||||
|
||||
Spectacle.deficit = lambda x: (x.slots-x.nrequests)*x.price
|
||||
return render(request, "inscription-bda.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.all()
|
||||
shows = Spectacle.objects.select_related().all()
|
||||
members = Participant.objects.all()
|
||||
choices = ChoixSpectacle.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
|
||||
|
@ -99,8 +150,8 @@ def do_tirage(request):
|
|||
total_sold = 0
|
||||
total_deficit = 0
|
||||
opera_deficit = 0
|
||||
for show in shows:
|
||||
deficit = show.deficit()
|
||||
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:
|
||||
|
@ -109,18 +160,23 @@ 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 not in members2:
|
||||
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 == "seguin":
|
||||
if False and request.user.username in ["seguin", "harazi"]:
|
||||
Attribution.objects.all().delete()
|
||||
for (show, members, _) in results:
|
||||
for (member, _, _, _) in members:
|
||||
|
@ -159,15 +215,27 @@ def do_resell(request, form):
|
|||
spectacle = form.cleaned_data["spectacle"]
|
||||
count = form.cleaned_data["count"]
|
||||
places = "2 places" if count == "2" else "une place"
|
||||
"""
|
||||
send_custom_mail("bda-revente@lists.ens.fr",
|
||||
"bda-revente",
|
||||
{"places": places,
|
||||
"spectacle": spectacle.title,
|
||||
"date": spectacle.date_no_seconds(),
|
||||
"lieu": spectacle.location,
|
||||
"prix": spectacle.price,
|
||||
"revendeur": request.user.get_full_name(),
|
||||
"revendeur_mail": request.user.email},
|
||||
from_email = request.user.email)
|
||||
"""
|
||||
mail = u"""Bonjour,
|
||||
|
||||
Je souhaite revendre %s pour %s le %s (%s) à %.02f€.
|
||||
Contactez moi par email si vous êtes intéressé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)
|
||||
send_mail("Revente de place: %s" % spectacle, mail,
|
||||
send_mail("%s" % spectacle, mail,
|
||||
request.user.email, ["bda-revente@lists.ens.fr"],
|
||||
fail_silently = True)
|
||||
fail_silently = False)
|
||||
return render(request, "bda-success.html", {"show": spectacle, "places": places})
|
||||
|
||||
@login_required
|
||||
|
|
|
@ -55,6 +55,9 @@ class EventOptionChoiceInline(admin.TabularInline):
|
|||
class EventOptionInline(admin.TabularInline):
|
||||
model = EventOption
|
||||
|
||||
class EventCommentFieldInline(admin.TabularInline):
|
||||
model = EventCommentField
|
||||
|
||||
class EventOptionAdmin(admin.ModelAdmin):
|
||||
inlines = [
|
||||
EventOptionChoiceInline,
|
||||
|
@ -63,6 +66,7 @@ class EventOptionAdmin(admin.ModelAdmin):
|
|||
class EventAdmin(admin.ModelAdmin):
|
||||
inlines = [
|
||||
EventOptionInline,
|
||||
EventCommentFieldInline,
|
||||
]
|
||||
|
||||
#from eav.forms import BaseDynamicEntityForm
|
||||
|
@ -253,6 +257,8 @@ admin.site.register(EventOption, EventOptionAdmin)
|
|||
admin.site.unregister(User)
|
||||
admin.site.register(User, UserProfileAdmin)
|
||||
admin.site.register(CofProfile)
|
||||
admin.site.register(Club)
|
||||
admin.site.register(CustomMail)
|
||||
admin.site.register(PetitCoursSubject)
|
||||
admin.site.register(PetitCoursAbility, PetitCoursAbilityAdmin)
|
||||
admin.site.register(PetitCoursAttribution, PetitCoursAttributionAdmin)
|
||||
|
|
|
@ -24,6 +24,11 @@ TYPE_COTIZ_CHOICES = (
|
|||
('exterieur', _(u"Extérieur")),
|
||||
)
|
||||
|
||||
TYPE_COMMENT_FIELD = (
|
||||
('text', _(u"Texte long")),
|
||||
('char', _(u"Texte court")),
|
||||
)
|
||||
|
||||
def choices_length (choices):
|
||||
return reduce (lambda m, choice: max (m, len (choice[0])), choices, 0)
|
||||
|
||||
|
@ -45,6 +50,7 @@ class CofProfile(models.Model):
|
|||
mailing_cof = models.BooleanField("Recevoir les mails COF", default = False)
|
||||
mailing_bda = models.BooleanField("Recevoir les mails BdA", default = False)
|
||||
mailing_bda_revente = models.BooleanField("Recevoir les mails de revente de places BdA", default = False)
|
||||
comments = models.TextField("Commentaires visibles uniquement par le Buro", blank = True)
|
||||
is_buro = models.BooleanField("Membre du Burô", default = False)
|
||||
petits_cours_accept = models.BooleanField("Recevoir des petits cours", default = False)
|
||||
petits_cours_remarques = models.TextField(_(u"Remarques et précisions pour les petits cours"),
|
||||
|
@ -62,6 +68,24 @@ def create_user_profile(sender, instance, created, **kwargs):
|
|||
CofProfile.objects.get_or_create(user = instance)
|
||||
post_save.connect(create_user_profile, sender = User)
|
||||
|
||||
class Club(models.Model):
|
||||
name = models.CharField("Nom", max_length = 200)
|
||||
description = models.TextField("Description")
|
||||
respos = models.ManyToManyField(User, related_name = "clubs_geres")
|
||||
membres = models.ManyToManyField(User, related_name = "clubs")
|
||||
|
||||
class CustomMail(models.Model):
|
||||
shortname = models.SlugField(max_length = 50, blank = False)
|
||||
title = models.CharField("Titre", max_length = 200, blank = False)
|
||||
content = models.TextField("Contenu", blank = False)
|
||||
comments = models.TextField("Informations contextuelles sur le mail", blank = True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Mails personnalisables"
|
||||
|
||||
def __unicode__(self):
|
||||
return u"%s: %s" % (self.shortname, self.title)
|
||||
|
||||
class Event(models.Model):
|
||||
title = models.CharField("Titre", max_length = 200)
|
||||
location = models.CharField("Lieu", max_length = 200)
|
||||
|
@ -77,6 +101,23 @@ class Event(models.Model):
|
|||
def __unicode__(self):
|
||||
return unicode(self.title)
|
||||
|
||||
class EventCommentField(models.Model):
|
||||
event = models.ForeignKey(Event, related_name = "commentfields")
|
||||
name = models.CharField("Champ", max_length = 200)
|
||||
fieldtype = models.CharField("Type", max_length = 10, choices = TYPE_COMMENT_FIELD, default = "text")
|
||||
default = models.TextField("Valeur par défaut", blank = True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Champ"
|
||||
|
||||
def __unicode__(self):
|
||||
return unicode(self.name)
|
||||
|
||||
class EventCommentValue(models.Model):
|
||||
commentfield = models.ForeignKey(EventCommentField, related_name = "values")
|
||||
registration = models.ForeignKey("EventRegistration", related_name = "comments")
|
||||
content = models.TextField("Contenu", blank = True, null = True)
|
||||
|
||||
class EventOption(models.Model):
|
||||
event = models.ForeignKey(Event, related_name = "options")
|
||||
name = models.CharField("Option", max_length = 200)
|
||||
|
@ -102,6 +143,7 @@ class EventRegistration(models.Model):
|
|||
user = models.ForeignKey(User)
|
||||
event = models.ForeignKey(Event)
|
||||
options = models.ManyToManyField(EventOptionChoice)
|
||||
filledcomments = models.ManyToManyField(EventCommentField, through = EventCommentValue)
|
||||
paid = models.BooleanField("A payé", default = False)
|
||||
|
||||
class Meta:
|
||||
|
|
|
@ -60,38 +60,45 @@ def _get_attrib_counter(user, matiere):
|
|||
counter.save()
|
||||
return counter
|
||||
|
||||
|
||||
|
||||
@buro_required
|
||||
def traitement(request, demande_id):
|
||||
demande = get_object_or_404(PetitCoursDemande, id = demande_id)
|
||||
if demande.niveau == "other":
|
||||
return _traitement_other(request, demande)
|
||||
if request.method == "POST":
|
||||
return _traitement_post(request, demande)
|
||||
proposals = {}
|
||||
proposed_for = {}
|
||||
unsatisfied = []
|
||||
attribdata = {}
|
||||
def _get_demande_candidates(demande, redo = False):
|
||||
for matiere in demande.matieres.all():
|
||||
candidates = PetitCoursAbility.objects.filter(matiere = matiere, niveau = demande.niveau)
|
||||
candidates = candidates.filter(user__profile__is_cof = True,
|
||||
user__profile__petits_cours_accept = True)
|
||||
if demande.agrege_requis:
|
||||
candidates = candidates.filter(agrege = True)
|
||||
candidates = candidates.values_list('user', flat = True).distinct().order_by('?').all()
|
||||
tuples = []
|
||||
for candidate in candidates:
|
||||
user = User.objects.get(pk = candidate)
|
||||
tuples.append((candidate, _get_attrib_counter(user, matiere)))
|
||||
if tuples:
|
||||
if redo:
|
||||
attributions = PetitCoursAttribution.objects.filter(demande = demande,
|
||||
matiere = matiere).all()
|
||||
for attrib in attributions:
|
||||
candidates = candidates.exclude(user = attrib.user)
|
||||
candidates = candidates.order_by('?').select_related().all()
|
||||
yield (matiere, candidates)
|
||||
|
||||
@buro_required
|
||||
def traitement(request, demande_id, redo = False):
|
||||
demande = get_object_or_404(PetitCoursDemande, id = demande_id)
|
||||
if demande.niveau == "other":
|
||||
return _traitement_other(request, demande, redo)
|
||||
if request.method == "POST":
|
||||
return _traitement_post(request, demande)
|
||||
proposals = {}
|
||||
proposed_for = {}
|
||||
unsatisfied = []
|
||||
attribdata = {}
|
||||
for matiere, candidates in _get_demande_candidates(demande, redo):
|
||||
if candidates:
|
||||
tuples = []
|
||||
for candidate in candidates:
|
||||
user = candidate.user
|
||||
tuples.append((candidate, _get_attrib_counter(user, matiere)))
|
||||
tuples = sorted(tuples, key = lambda c: c[1].count)
|
||||
candidates, _ = zip(*tuples)
|
||||
attribdata[matiere.id] = []
|
||||
candidates = candidates[0:min(3, len(candidates))]
|
||||
attribdata[matiere.id] = []
|
||||
proposals[matiere] = []
|
||||
for candidate in candidates:
|
||||
user = User.objects.get(pk = candidate)
|
||||
user = candidate.user
|
||||
proposals[matiere].append(user)
|
||||
attribdata[matiere.id].append(user.id)
|
||||
if user not in proposed_for:
|
||||
|
@ -100,10 +107,15 @@ def traitement(request, demande_id):
|
|||
proposed_for[user].append(matiere)
|
||||
else:
|
||||
unsatisfied.append(matiere)
|
||||
return _finalize_traitement(request, demande, proposals, proposed_for, unsatisfied, attribdata)
|
||||
return _finalize_traitement(request, demande, proposals,
|
||||
proposed_for, unsatisfied, attribdata, redo)
|
||||
|
||||
@buro_required
|
||||
def retraitement(request, demande_id):
|
||||
return traitement(request, demande_id, redo = True)
|
||||
|
||||
def _finalize_traitement(request, demande, proposals, proposed_for,
|
||||
unsatisfied, attribdata, redo = False):
|
||||
unsatisfied, attribdata, redo = False, errors = None):
|
||||
proposals = proposals.items()
|
||||
proposed_for = proposed_for.items()
|
||||
attribdata = attribdata.items()
|
||||
|
@ -121,6 +133,7 @@ def _finalize_traitement(request, demande, proposals, proposed_for,
|
|||
"mainmail": mainmail,
|
||||
"attribdata": base64.b64encode(simplejson.dumps(attribdata)),
|
||||
"redo": redo,
|
||||
"errors": errors,
|
||||
})
|
||||
|
||||
def _generate_eleve_email(demande, proposed_for):
|
||||
|
@ -130,15 +143,45 @@ def _generate_eleve_email(demande, proposed_for):
|
|||
proposed_mails.append((user, msg))
|
||||
return proposed_mails
|
||||
|
||||
def _traitement_other_post(request, demande):
|
||||
for matiere in demande.matieres.all():
|
||||
for choice_id in range(3):
|
||||
choice = request.POST["proposal-%d-%d"] % (matiere.id, choice_id)]:
|
||||
pass
|
||||
return None
|
||||
return _finalize_traitement(request, demande, proposals, proposed_for, unsatisfied, attribdata)
|
||||
def _traitement_other_preparing(request, demande):
|
||||
redo = "redo" in request.POST
|
||||
unsatisfied = []
|
||||
proposals = {}
|
||||
proposed_for = {}
|
||||
attribdata = {}
|
||||
errors = []
|
||||
for matiere, candidates in _get_demande_candidates(demande, redo):
|
||||
if candidates:
|
||||
candidates = dict([(candidate.user.id, candidate.user)
|
||||
for candidate in candidates])
|
||||
attribdata[matiere.id] = []
|
||||
proposals[matiere] = []
|
||||
for choice_id in range(min(3, len(candidates))):
|
||||
choice = int(request.POST["proposal-%d-%d" % (matiere.id, choice_id)])
|
||||
if choice == -1:
|
||||
continue
|
||||
if choice not in candidates:
|
||||
errors.append(u"Choix invalide pour la proposition %d en %s" % (choice_id + 1, matiere))
|
||||
continue
|
||||
user = candidates[choice]
|
||||
if user in proposals[matiere]:
|
||||
errors.append(u"La proposition %d en %s est un doublon" % (choice_id + 1, matiere))
|
||||
continue
|
||||
proposals[matiere].append(user)
|
||||
attribdata[matiere.id].append(user.id)
|
||||
if user not in proposed_for:
|
||||
proposed_for[user] = [matiere]
|
||||
else:
|
||||
proposed_for[user].append(matiere)
|
||||
if not proposals[matiere]:
|
||||
errors.append(u"Aucune proposition pour %s" % (matiere,))
|
||||
elif len(proposals[matiere]) < 3:
|
||||
errors.append(u"Seulement %d proposition%s pour %s" % (len(proposals[matiere]), "s" if len(proposals[matiere]) > 1 else "", matiere))
|
||||
else:
|
||||
unsatisfied.append(matiere)
|
||||
return _finalize_traitement(request, demande, proposals, proposed_for, unsatisfied, attribdata, errors = errors)
|
||||
|
||||
def _traitement_other(request, demande):
|
||||
def _traitement_other(request, demande, redo):
|
||||
if request.method == "POST":
|
||||
if "preparing" in request.POST:
|
||||
return _traitement_other_preparing(request, demande)
|
||||
|
@ -148,13 +191,7 @@ def _traitement_other(request, demande):
|
|||
proposed_for = {}
|
||||
unsatisfied = []
|
||||
attribdata = {}
|
||||
for matiere in demande.matieres.all():
|
||||
candidates = PetitCoursAbility.objects.filter(matiere = matiere, niveau = demande.niveau)
|
||||
candidates = candidates.filter(user__profile__is_cof = True,
|
||||
user__profile__petits_cours_accept = True)
|
||||
if demande.agrege_requis:
|
||||
candidates = candidates.filter(agrege = True)
|
||||
candidates = candidates.order_by('?').select_related().all()
|
||||
for matiere, candidates in _get_demande_candidates(demande, redo):
|
||||
if candidates:
|
||||
tuples = []
|
||||
for candidate in candidates:
|
||||
|
@ -243,49 +280,6 @@ def _traitement_post(request, demande):
|
|||
"redo": redo,
|
||||
})
|
||||
|
||||
@buro_required
|
||||
def retraitement(request, demande_id):
|
||||
demande = get_object_or_404(PetitCoursDemande, id = demande_id)
|
||||
if request.method == "POST":
|
||||
return _traitement_post(request, demande)
|
||||
if demande.niveau == "other":
|
||||
return _traitement_other(request, demande)
|
||||
proposals = {}
|
||||
proposed_for = {}
|
||||
unsatisfied = []
|
||||
attribdata = {}
|
||||
for matiere in demande.matieres.all():
|
||||
candidates = PetitCoursAbility.objects.filter(matiere = matiere, niveau = demande.niveau)
|
||||
candidates = candidates.filter(user__profile__is_cof = True,
|
||||
user__profile__petits_cours_accept = True)
|
||||
if demande.agrege_requis:
|
||||
candidates = candidates.filter(agrege = True)
|
||||
attributions = PetitCoursAttribution.objects.filter(demande = demande, matiere = matiere).all()
|
||||
for attrib in attributions:
|
||||
candidates = candidates.exclude(user = attrib.user)
|
||||
candidates = candidates.values_list('user', flat = True).distinct().order_by('?').all()
|
||||
tuples = []
|
||||
for candidate in candidates:
|
||||
user = User.objects.get(pk = candidate)
|
||||
tuples.append((candidate, _get_attrib_counter(user, matiere)))
|
||||
if tuples:
|
||||
tuples = sorted(tuples, key = lambda c: c[1].count)
|
||||
candidates, _ = zip(*tuples)
|
||||
attribdata[matiere.id] = []
|
||||
candidates = candidates[0:min(3, len(candidates))]
|
||||
proposals[matiere] = []
|
||||
for candidate in candidates:
|
||||
user = User.objects.get(pk = candidate)
|
||||
proposals[matiere].append(user)
|
||||
attribdata[matiere.id].append(user.id)
|
||||
if user not in proposed_for:
|
||||
proposed_for[user] = [matiere]
|
||||
else:
|
||||
proposed_for[user].append(matiere)
|
||||
else:
|
||||
unsatisfied.append(matiere)
|
||||
return _finalize_traitement(request, demande, proposals, proposed_for, unsatisfied, attribdata, redo = True)
|
||||
|
||||
class BaseMatieresFormSet(BaseInlineFormSet):
|
||||
def clean(self):
|
||||
super(BaseMatieresFormSet, self).clean()
|
||||
|
|
|
@ -2,9 +2,12 @@ from django.contrib.sites.models import Site
|
|||
from django.conf import settings
|
||||
from django_cas.backends import CASBackend, _verify as CASverify
|
||||
from django_cas.models import User
|
||||
from django.contrib.auth.models import User as DjangoUser
|
||||
from django.db import models, connection
|
||||
from django.core.mail import send_mail
|
||||
from django.template import Template, Context
|
||||
|
||||
from gestioncof.models import CofProfile
|
||||
from gestioncof.models import CofProfile, CustomMail
|
||||
|
||||
class COFCASBackend(CASBackend):
|
||||
def authenticate_cas(self, ticket, service, request):
|
||||
|
@ -76,3 +79,16 @@ def unlock_tables(*models):
|
|||
return row
|
||||
|
||||
unlock_table = unlock_tables
|
||||
|
||||
def send_custom_mail(to, shortname, context = None, from_email = "cof@ens.fr"):
|
||||
if context is None: context = {}
|
||||
if isinstance(to, DjangoUser):
|
||||
context["nom"] = to.get_full_name()
|
||||
context["prenom"] = to.first_name
|
||||
to = to.email
|
||||
mail = CustomMail.objects.get(shortname = shortname)
|
||||
template = Template(mail.content)
|
||||
message = template.render(Context(context))
|
||||
send_mail (mail.title, message,
|
||||
from_email, [to],
|
||||
fail_silently = True)
|
||||
|
|
|
@ -15,10 +15,11 @@ from django.contrib.auth.views import login as django_login_view
|
|||
|
||||
from gestioncof.models import Survey, SurveyQuestion, SurveyQuestionAnswer, SurveyAnswer
|
||||
from gestioncof.models import Event, EventOption, EventOptionChoice, EventRegistration
|
||||
from gestioncof.models import EventCommentField, EventCommentValue
|
||||
from gestioncof.models import CofProfile, Clipper
|
||||
from gestioncof.decorators import buro_required, cof_required
|
||||
from gestioncof.widgets import TriStateCheckbox
|
||||
from gestioncof.shared import lock_table, unlock_table
|
||||
from gestioncof.shared import lock_table, unlock_table, send_custom_mail
|
||||
|
||||
@login_required
|
||||
def home(request):
|
||||
|
@ -205,6 +206,17 @@ def get_event_form_choices(event, form):
|
|||
all_choices.append(choice)
|
||||
return all_choices
|
||||
|
||||
def update_event_form_comments(event, form, registration):
|
||||
for commentfield_id, value in form.comments():
|
||||
field = get_object_or_404(EventCommentField, id = commentfield_id,
|
||||
event = event)
|
||||
if value == field.default:
|
||||
continue
|
||||
(storage, _) = EventCommentValue.objects.get_or_create(commentfield = field,
|
||||
registration = registration)
|
||||
storage.content = value
|
||||
storage.save()
|
||||
|
||||
@login_required
|
||||
def event(request, event_id):
|
||||
event = get_object_or_404(Event, id = event_id)
|
||||
|
@ -426,6 +438,7 @@ class RegistrationProfileForm(forms.ModelForm):
|
|||
'mailing_cof',
|
||||
'mailing_bda',
|
||||
'mailing_bda_revente',
|
||||
'comments'
|
||||
]
|
||||
|
||||
def save(self, *args, **kw):
|
||||
|
@ -445,7 +458,7 @@ class RegistrationProfileForm(forms.ModelForm):
|
|||
|
||||
class Meta:
|
||||
model = CofProfile
|
||||
fields = ("login_clipper", "num", "phone", "occupation", "departement", "is_cof", "type_cotiz", "mailing_cof", "mailing_bda", "mailing_bda_revente",)
|
||||
fields = ("login_clipper", "num", "phone", "occupation", "departement", "is_cof", "type_cotiz", "mailing_cof", "mailing_bda", "mailing_bda_revente", "comments")
|
||||
|
||||
def registration_set_ro_fields(user_form, profile_form):
|
||||
user_form.fields['username'].widget.attrs['readonly'] = True
|
||||
|
@ -487,15 +500,16 @@ def registration_form(request, login_clipper = None, username = None):
|
|||
return render(request, "registration_form.html", {"user_form": user_form, "profile_form": profile_form, "member": member, "login_clipper": login_clipper})
|
||||
|
||||
STATUS_CHOICES = (('no','Non'),
|
||||
('wait','Attente paiement'),
|
||||
('paid','Payé'),)
|
||||
('wait','Oui mais attente paiement'),
|
||||
('paid','Oui payé'),)
|
||||
class AdminEventForm(forms.Form):
|
||||
status = forms.ChoiceField(label = "Inscription", choices = STATUS_CHOICES, widget = RadioSelect)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
event = kwargs.pop("event")
|
||||
self.event = event
|
||||
current_choices = kwargs.pop("current_choices", None)
|
||||
registration = kwargs.pop("current_registration", None)
|
||||
current_choices = registration.options.all() if registration is not None else []
|
||||
paid = kwargs.pop("paid", None)
|
||||
if paid == True:
|
||||
kwargs["initial"] = {"status":"paid"}
|
||||
|
@ -505,12 +519,12 @@ class AdminEventForm(forms.Form):
|
|||
kwargs["initial"] = {"status":"no"}
|
||||
super(AdminEventForm, self).__init__(*args, **kwargs)
|
||||
choices = {}
|
||||
if current_choices:
|
||||
for choice in current_choices.all():
|
||||
if choice.event_option.id not in choices:
|
||||
choices[choice.event_option.id] = [choice.id]
|
||||
else:
|
||||
choices[choice.event_option.id].append(choice.id)
|
||||
comments = {}
|
||||
for choice in current_choices:
|
||||
if choice.event_option.id not in choices:
|
||||
choices[choice.event_option.id] = [choice.id]
|
||||
else:
|
||||
choices[choice.event_option.id].append(choice.id)
|
||||
all_choices = choices
|
||||
for option in event.options.all():
|
||||
choices = [(choice.id, choice.value) for choice in option.choices.all()]
|
||||
|
@ -530,12 +544,31 @@ class AdminEventForm(forms.Form):
|
|||
initial = initial)
|
||||
field.option_id = option.id
|
||||
self.fields["option_%d" % option.id] = field
|
||||
for commentfield in event.commentfields.all():
|
||||
initial = commentfield.default
|
||||
if registration is not None:
|
||||
try:
|
||||
initial = registration.comments.get(commentfield = commentfield).content
|
||||
except EventCommentValue.DoesNotExist:
|
||||
pass
|
||||
widget = forms.Textarea if commentfield.fieldtype == "text" else forms.TextInput
|
||||
field = forms.CharField(label = commentfield.name,
|
||||
widget = widget,
|
||||
required = False,
|
||||
initial = initial)
|
||||
field.comment_id = commentfield.id
|
||||
self.fields["comment_%d" % commentfield.id] = field
|
||||
|
||||
def choices(self):
|
||||
for name, value in self.cleaned_data.items():
|
||||
if name.startswith('option_'):
|
||||
yield (self.fields[name].option_id, value)
|
||||
|
||||
def comments(self):
|
||||
for name, value in self.cleaned_data.items():
|
||||
if name.startswith('comment_'):
|
||||
yield (self.fields[name].comment_id, value)
|
||||
|
||||
@buro_required
|
||||
def registration_form2(request, login_clipper = None, username = None):
|
||||
events = Event.objects.filter(old = False).all()
|
||||
|
@ -571,7 +604,7 @@ def registration_form2(request, login_clipper = None, username = None):
|
|||
for event in events:
|
||||
try:
|
||||
current_registration = EventRegistration.objects.get(user = member, event = event)
|
||||
form = AdminEventForm(event = event, current_choices = current_registration.options, paid = current_registration.paid)
|
||||
form = AdminEventForm(event = event, current_registration = current_registration, paid = current_registration.paid)
|
||||
except EventRegistration.DoesNotExist:
|
||||
form = AdminEventForm(event = event)
|
||||
event_forms.append(form)
|
||||
|
@ -618,10 +651,14 @@ def registration(request):
|
|||
if user_form.is_valid() and profile_form.is_valid() and not any([not form.is_valid() for form in event_forms]):
|
||||
member = user_form.save()
|
||||
(profile, _) = CofProfile.objects.get_or_create(user = member)
|
||||
was_cof = profile.is_cof
|
||||
request_dict["num"] = profile.num
|
||||
profile_form = RegistrationProfileForm(request_dict, instance = profile)
|
||||
profile_form.is_valid()
|
||||
profile_form.save()
|
||||
(profile, _) = CofProfile.objects.get_or_create(user = member)
|
||||
if profile.is_cof and not was_cof:
|
||||
send_custom_mail(member, "bienvenue")
|
||||
for form in event_forms:
|
||||
if form.cleaned_data['status'] == 'no':
|
||||
try:
|
||||
|
@ -631,10 +668,18 @@ def registration(request):
|
|||
pass
|
||||
continue
|
||||
all_choices = get_event_form_choices(form.event, form)
|
||||
(current_registration, _) = EventRegistration.objects.get_or_create(user = member, event = form.event)
|
||||
(current_registration, created_reg) = EventRegistration.objects.get_or_create(user = member, event = form.event)
|
||||
update_event_form_comments(event, form, current_registration)
|
||||
current_registration.options = all_choices
|
||||
current_registration.paid = (form.cleaned_data['status'] == 'paid')
|
||||
current_registration.save()
|
||||
if event.title == "Mega 2014" and created_reg:
|
||||
field = EventCommentField.objects.get(event = event, name = "Commentaires")
|
||||
try:
|
||||
comments = EventCommentValue.objects.get(commentfield = field, registration = current_registration).content
|
||||
except EventCommentValue.DoesNotExist:
|
||||
comments = field.default
|
||||
send_custom_mail(member, "mega", {"remarques": comments})
|
||||
success = True
|
||||
return render(request, "registration_post.html", {"success": success, "user_form": user_form, "profile_form": profile_form, "member": member, "login_clipper": login_clipper, "event_forms": event_forms})
|
||||
else:
|
||||
|
@ -653,54 +698,76 @@ def export_members(request):
|
|||
|
||||
return response
|
||||
|
||||
def csv_export_mega(filename, qs):
|
||||
response = HttpResponse(mimetype = 'text/csv')
|
||||
response['Content-Disposition'] = 'attachment; filename=' + filename
|
||||
writer = unicodecsv.UnicodeWriter(response)
|
||||
|
||||
for reg in qs.all():
|
||||
user = reg.user
|
||||
profile = user.get_profile()
|
||||
comments = "---".join([comment.content for comment in reg.comments.all()])
|
||||
bits = [user.username, user.first_name, user.last_name, user.email, profile.phone, profile.num, profile.comments if profile.comments else "", comments]
|
||||
writer.writerow([unicode(bit) for bit in bits])
|
||||
|
||||
return response
|
||||
|
||||
@buro_required
|
||||
def export_mega_remarksonly(request):
|
||||
filename = 'remarques_mega_2014.csv'
|
||||
response = HttpResponse(mimetype = 'text/csv')
|
||||
response['Content-Disposition'] = 'attachment; filename=' + filename
|
||||
writer = unicodecsv.UnicodeWriter(response)
|
||||
|
||||
event = Event.objects.get(title = "Mega 2014")
|
||||
commentfield = event.commentfields.get(name = "Commentaires")
|
||||
for val in commentfield.values.all():
|
||||
reg = val.registration
|
||||
user = reg.user
|
||||
profile = user.get_profile()
|
||||
bits = [user.username, user.first_name, user.last_name, user.email, profile.phone, profile.num, profile.comments, val.content]
|
||||
writer.writerow([unicode(bit) for bit in bits])
|
||||
|
||||
return response
|
||||
|
||||
@buro_required
|
||||
def export_mega_bytype(request, type):
|
||||
types = {"orga-actif": "Orga actif",
|
||||
"orga-branleur": "Orga branleur",
|
||||
"conscrit-eleve": "Conscrit élève",
|
||||
"conscrit-etudiant": "Conscrit étudiant"}
|
||||
|
||||
if type not in types:
|
||||
raise Http404
|
||||
|
||||
event = Event.objects.get(title = "Mega 2014")
|
||||
type_option = event.options.get(name = "Type")
|
||||
participant_type = type_option.choices.get(value = types[type]).id
|
||||
qs = EventRegistration.objects.filter(event = event).filter(options__id__exact = participant_type)
|
||||
return csv_export_mega(type + '_mega_2014.csv', qs)
|
||||
|
||||
@buro_required
|
||||
def export_mega_orgas(request):
|
||||
response = HttpResponse(mimetype = 'text/csv')
|
||||
response['Content-Disposition'] = 'attachment; filename=orgas_mega.csv'
|
||||
|
||||
writer = unicodecsv.UnicodeWriter(response)
|
||||
event = Event.objects.get(title = "MEGA")
|
||||
event = Event.objects.get(title = "Mega 2014")
|
||||
type_option = event.options.get(name = "Type")
|
||||
participant_type = type_option.choices.get(value = "Participant").id
|
||||
for reg in EventRegistration.objects.filter(event = event).exclude(options__id__exact = participant_type).all():
|
||||
user = reg.user
|
||||
profile = user.get_profile()
|
||||
bits = [user.username, user.first_name, user.last_name, user.email, profile.phone, profile.num]
|
||||
writer.writerow([unicode(bit) for bit in bits])
|
||||
participant_type_a = type_option.choices.get(value = "Conscrit étudiant").id
|
||||
participant_type_b = type_option.choices.get(value = "Conscrit élève").id
|
||||
qs = EventRegistration.objects.filter(event = event).exclude(options__id__in = (participant_type_a, participant_type_b))
|
||||
return csv_export_mega('orgas_mega_2014.csv', qs)
|
||||
|
||||
return response
|
||||
|
||||
@buro_required
|
||||
def export_mega_participants(request):
|
||||
response = HttpResponse(mimetype = 'text/csv')
|
||||
response['Content-Disposition'] = 'attachment; filename=participants_mega.csv'
|
||||
|
||||
writer = unicodecsv.UnicodeWriter(response)
|
||||
event = Event.objects.get(title = "MEGA")
|
||||
event = Event.objects.get(title = "Mega 2014")
|
||||
type_option = event.options.get(name = "Type")
|
||||
participant_type = type_option.choices.get(value = "Participant").id
|
||||
for reg in EventRegistration.objects.filter(event = event).filter(options__id__exact = participant_type).all():
|
||||
user = reg.user
|
||||
profile = user.get_profile()
|
||||
bits = [user.username, user.first_name, user.last_name, user.email, profile.phone, profile.num]
|
||||
writer.writerow([unicode(bit) for bit in bits])
|
||||
|
||||
return response
|
||||
participant_type_a = type_option.choices.get(value = "Conscrit étudiant").id
|
||||
participant_type_b = type_option.choices.get(value = "Conscrit élève").id
|
||||
qs = EventRegistration.objects.filter(event = event).filter(options__id__in = (participant_type_a, participant_type_b))
|
||||
return csv_export_mega('participants_mega_2014.csv', qs)
|
||||
|
||||
@buro_required
|
||||
def export_mega(request):
|
||||
response = HttpResponse(mimetype = 'text/csv')
|
||||
response['Content-Disposition'] = 'attachment; filename=all_mega.csv'
|
||||
|
||||
writer = unicodecsv.UnicodeWriter(response)
|
||||
event = Event.objects.filter(title = "MEGA")
|
||||
for reg in EventRegistration.objects.filter(event = event).order_by("user__username").all():
|
||||
user = reg.user
|
||||
profile = user.get_profile()
|
||||
bits = [user.username, user.first_name, user.last_name, user.email, profile.phone, profile.num]
|
||||
writer.writerow([unicode(bit) for bit in bits])
|
||||
|
||||
return response
|
||||
event = Event.objects.filter(title = "Mega 2014")
|
||||
qs = EventRegistration.objects.filter(event = event).order_by("user__username")
|
||||
return csv_export_mega('all_mega_2014.csv', qs)
|
||||
|
||||
@buro_required
|
||||
def utile_cof(request):
|
||||
|
|
0
manage.py
Executable file → Normal file
0
manage.py
Executable file → Normal file
|
@ -176,8 +176,8 @@ fieldset legend {
|
|||
}
|
||||
|
||||
#main-container {
|
||||
max-width: 90%;
|
||||
width: 800px;
|
||||
max-width: 95%;
|
||||
width: 1000px;
|
||||
margin: 7em auto;
|
||||
display: block;
|
||||
}
|
||||
|
@ -588,3 +588,47 @@ pre code {
|
|||
max-height: 340px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
|
||||
/* Louis pour etat places*/
|
||||
|
||||
|
||||
.etat-bda td {
|
||||
border:1px solid #666;
|
||||
padding:4px;
|
||||
}
|
||||
|
||||
.etat-bda tr:nth-child(even) {background: #CCC}
|
||||
|
||||
.greenratio {
|
||||
background-color: #3F3;
|
||||
border: 5px solid #ccc;
|
||||
border: 5px solid rgba(0, 0, 0, 0.15);
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.orangeratio {
|
||||
background-color: #FF3;
|
||||
border: 5px solid #ccc;
|
||||
border: 5px solid rgba(0, 0, 0, 0.15);
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.redratio {
|
||||
background-color: #F33;
|
||||
border: 5px solid #ccc;
|
||||
border: 5px solid rgba(0, 0, 0, 0.15);
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
th[data-sort]{
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
tr.awesome{
|
||||
color: red;
|
||||
}
|
|
@ -10,6 +10,7 @@
|
|||
<h2>Token : {{ token }}</h2>
|
||||
<h2>Placés : {{ total_slots }} ; Déçus : {{ total_losers }}</h2>
|
||||
{% if user.get_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>
|
||||
|
||||
{% for show, members, losers in results %}
|
||||
<div class="attribresult">
|
||||
|
|
|
@ -1,11 +1,49 @@
|
|||
{% 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>
|
||||
<h2>État des inscriptions BdA</h2>
|
||||
<table class="etat-bda">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-sort="string">Titre</th>
|
||||
<th data-sort="int">Date</th>
|
||||
<th data-sort="string">Lieu</th>
|
||||
<th data-sort="int">Places</th>
|
||||
<th data-sort="int">Demandes</th>
|
||||
<th data-sort="float">Ratio</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for spectacle in spectacles %}
|
||||
<tr>
|
||||
<td>{{ spectacle.title }}</td>
|
||||
<td data-sort-value="{{ spectacle.timestamp }}">{{ spectacle.date_no_seconds }}</td>
|
||||
<td data-sort-value="{{ spectacle.location }}">{{ spectacle.location }}</td>
|
||||
<td data-sort-value="{{ spectacle.slots }}">{{ spectacle.slots }} places</td>
|
||||
<td data-sort-value="{{ spectacle.total }}">{{ spectacle.total }} demandes</td>
|
||||
<td data-sort-value="{{ spectacle.ratio |stringformat:".3f" }}">
|
||||
<div class={% if spectacle.ratio < 1.0 %}
|
||||
"greenratio"
|
||||
{% else %}
|
||||
{% if spectacle.ratio < 2.5 %}
|
||||
"orangeratio"
|
||||
{% else %}
|
||||
"redratio"
|
||||
{% endif %}
|
||||
{% endif %}>
|
||||
{{ spectacle.ratio |floatformat }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</tbody>
|
||||
</table>
|
||||
<strong>Total : <u>{{ total }} demandes</u></strong>
|
||||
<script type="text/javascript" src="/gestion/media/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/gestion/media/js/joequery-Stupid-Table-Plugin/stupidtable.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(function(){
|
||||
$("table.etat-bda").stupidtable();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -91,10 +91,13 @@ var django = {
|
|||
})(django.jQuery);
|
||||
</script>
|
||||
|
||||
<h2>Inscription au tirage au sort du BDA</h2>
|
||||
<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 'bda-tirage-inscription' %}">
|
||||
{% csrf_token %}
|
||||
{% include "inscription-formset.html" %}
|
||||
|
@ -104,6 +107,7 @@ var django = {
|
|||
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 />
|
||||
|
|
|
@ -22,13 +22,32 @@
|
|||
</ul>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if user.profile.is_cof %}
|
||||
<h3>BdA</h3>
|
||||
<ul>
|
||||
<li><a href="{% url "bda-tirage-inscription" %}">Inscription au tirage au sort du BdA</a></li>
|
||||
<li><a href="{% url "bda.views.etat_places" %}">Etat des demandes</a></li>
|
||||
<!-- <li><a href="{% url "bda.views.revente" %}">Revente de places BdA (premier tirage)</a></li> -->
|
||||
<!-- <li><a href="{% url "bda2.views.revente" %}">Revente de places BdA (second tirage)</a></li> -->
|
||||
Premier tirage
|
||||
<!-- <li><a href="{% url "bda-tirage-inscription" %}">Inscription au premier tirage au sort du BdA</a></li> -->
|
||||
<li><a href="{% url "bda-etat-places" %}">État des demandes</a></li>
|
||||
<li><a href="{% url "bda-places-attribuees" %}">Mes places du premier tirage</a></li>
|
||||
<li><a href="{% url "bda-revente" %}">Revendre une place du premier tirage</a></li>
|
||||
<br>
|
||||
<!-- Second tirage
|
||||
<li><a href="{% url "bda2-tirage-inscription" %}">Inscription au deuxième tirage au sort du BdA</a></li>
|
||||
<li><a href="{% url "bda2-etat-places" %}">État des demandes</a></li>
|
||||
<li><a href="{% url "bda2-places-attribuees" %}">Mes places du deuxième tirage</a></li>
|
||||
<li><a href="{% url "bda2-revente" %}">Revendre une place du deuxième tirage</a></li>
|
||||
<br>
|
||||
-->
|
||||
Troisième tirage
|
||||
<li><a href="{% url "bda3-tirage-inscription" %}">Inscription au troisième tirage au sort du BdA</a></li>
|
||||
<li><a href="{% url "bda3-etat-places" %}">État des demandes</a></li>
|
||||
<li><a href="{% url "bda3-places-attribuees" %}">Mes places du troisième tirage</a></li>
|
||||
<li><a href="{% url "bda3-revente" %}">Revendre une place du troisième tirage</a></li>
|
||||
<br>
|
||||
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
<h3>Divers</h3>
|
||||
<ul>
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
|
||||
<h3>Mega</h3>
|
||||
<ul>
|
||||
<li><a href="{% url 'gestioncof.views.export_mega_participants' %}">Export des participants</a></li>
|
||||
<li><a href="{% url 'gestioncof.views.export_mega_orgas' %}">Export des orgas</a></li>
|
||||
<li><a href="{% url 'gestioncof.views.export_mega_participants' %}">Export des non-orgas uniquement</a></li>
|
||||
<li><a href="{% url 'gestioncof.views.export_mega_orgas' %}">Export des orgas uniquement</a></li>
|
||||
<li><a href="{% url 'gestioncof.views.export_mega' %}">Export de tout le monde</a></li>
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
|
Loading…
Reference in a new issue