Update while working on Django 1.5 upgrade

This commit is contained in:
root 2013-09-05 22:20:52 +02:00
parent 2479b0a24d
commit 9c989d886b
25 changed files with 1112 additions and 357 deletions

View file

View file

@ -1,6 +1,6 @@
import os, sys
sys.path.append (os.path.expanduser ('~gestion/www'))
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
os.environ['DJANGO_SETTINGS_MODULE'] = 'cof.settings'
import django.core.handlers.wsgi

View file

@ -1,14 +1,143 @@
# coding: utf-8
from django.core.mail import send_mail
from django.contrib.contenttypes.models import ContentType
from django.contrib import admin
from bda.models import Spectacle, Participant, ChoixSpectacle
from django.db.models import Sum
from bda.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 = [ChoixSpectacleInline]
inlines = [AttributionInline]
def nb_places(self, obj):
return len(obj.attribution_set.all())
nb_places.short_description = "Nombre de places"
def total(self, obj):
tot = obj.attributions.aggregate(total = Sum('price'))['total']
if tot: return u"%.02f" % tot
else: return u"0 €"
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@clipper.ens.fr", [member.user.email],
fail_silently = True)
count = len(queryset.all())
if count == 1:
message_bit = u"1 membre a"
plural = ""
else:
message_bit = u"%d membres ont" % count
plural = "s"
self.message_user(request, u"%s été informé%s avec succès." % (message_bit, plural))
send_choices.short_description = u"Envoyer les choix par mail"
def send_attribs(self, request, queryset):
for member in queryset.all():
attribs = member.attributions.all()
if len(attribs) == 0:
continue
mail = u"""Cher(e) %s,
Tu t'es inscrit(e) pour le tirage au sort du BdA. Tu as été sélectionné(e)
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).
*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.
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 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).
Culturellement vôtre,
--
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 ("Places de spectacle (BdA du COF)", mail,
"bda@clipper.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)
list_display = ("participant", "spectacle", "priority", "double", "autoquit")
list_filter = ("double", "autoquit")
search_fields = ('participant__user__username', 'participant__user__first_name', 'participant__user__last_name')
admin.site.register(Spectacle)
admin.site.register(Salle)
admin.site.register(Participant, ParticipantAdmin)
admin.site.register(Attribution, AttributionAdmin)
admin.site.register(ChoixSpectacle, ChoixSpectacleAdmin)

View file

@ -1,6 +1,8 @@
# coding: utf-8
from django.conf import settings
from django.db.models import Max
import random
class Algorithm(object):
@ -10,37 +12,41 @@ class Algorithm(object):
origranks = None
double = None
def __init__(self, shows, members):
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.double = {}
self.choices = {}
for member in members:
ranks = {}
double = {}
for i in range(1, settings.NUM_CHOICES + 1):
choice = getattr(member, "choice%d" % i)
if not choice:
continue
# Noter les doubles demandes
if choice in double:
double[choice] = True
else:
showdict[choice].requests.append(member)
ranks[choice] = i
double[choice] = False
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.double[member] = double
self.choices[member] = member_choices
self.origranks[member] = dict(ranks)
def IncrementRanks(self, member, currank, increment = 1):
@ -52,30 +58,37 @@ class Algorithm(object):
l.append((member,
self.ranks[member][show],
self.origranks[member][show],
self.double[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 = []
for show in self.shows:
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 = {}
for i in range(1, settings.NUM_CHOICES + 1):
groups[i] = []
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, settings.NUM_CHOICES + 1):
for i in range(1, self.max_group + 1):
group = list(groups[i])
random.shuffle(group)
for member in group:
if self.double[member][show]: # double
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 member.autoquit and len(winners) < show.slots:
elif not self.choices[member][show].autoquit and len(winners) < show.slots:
self.appendResult(winners, member, show)
self.appendResult(losers, member, show)
else:

View file

@ -5,13 +5,20 @@ 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.CharField ("Lieu", max_length = 300,
blank = True, null = True)
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)
@ -22,12 +29,25 @@ class Spectacle (models.Model):
def __repr__ (self):
return u"[%s]" % self.__unicode__()
def date_no_seconds(self):
return self.date.strftime('%d %b %Y %H:%M')
def __unicode__ (self):
return u"%s - %s @ %s" % (self.title, self.date, self.location)
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.ForeignKey(User, unique = True)
choices = models.ManyToManyField(Spectacle, through = "ChoixSpectacle")
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)
@ -40,5 +60,14 @@ class ChoixSpectacle (models.Model):
autoquit = models.BooleanField("Abandon<sup>2</sup>")
class Meta:
ordering = ("priority",)
#unique_together = (("participant", "spectacle",),)
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,15 +1,24 @@
# coding: utf-8
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 gestioncof.shared import render_page
from bda.models import Spectacle, Participant, ChoixSpectacle
from django.core.mail import send_mail
import time
from gestioncof.decorators import cof_required, buro_required
from bda.models import Spectacle, Participant, ChoixSpectacle, Attribution
from bda.algorithm import Algorithm
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
@ -24,17 +33,161 @@ class BaseBdaFormSet(BaseInlineFormSet):
raise forms.ValidationError("Vous ne pouvez pas vous inscrire deux fois pour le même spectacle.")
spectacles.append(spectacle)
@login_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'))
spectacles = Spectacle.objects.all()
spectacles_dict = {}
total = 0
for spectacle in spectacles:
spectacle.total = 0
spectacles_dict[spectacle.id] = spectacle
for spectacle in spectacles1:
spectacles_dict[spectacle["spectacle"]].total += spectacle["total"]
total += spectacle["total"]
for spectacle in spectacles2:
spectacles_dict[spectacle["spectacle"]].total += spectacle["total"]
total += spectacle["total"]
return render(request, "etat-places.html", {"spectacles": spectacles, "total": total})
@cof_required
def inscription(request):
if 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 "})
BdaFormSet = inlineformset_factory(Participant, ChoixSpectacle, fields = ("spectacle","double","autoquit","priority",), formset = BaseBdaFormSet)
participant, created = Participant.objects.get_or_create(user = request.user)
success = 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
formset = BdaFormSet(instance = participant)
else:
formset = BdaFormSet(instance = participant)
return render_page(request, {"formset": formset, "success": success}, "inscription-bda.html")
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
def do_tirage(request):
form = TokenForm(request.POST)
if not form.is_valid():
return tirage(request)
data = {}
shows = Spectacle.objects.all()
members = Participant.objects.all()
choices = ChoixSpectacle.objects.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 in shows:
deficit = show.deficit()
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
if request.user.is_authenticated():
members2 = {}
for (show, members, _) in results:
for (member, _, _, _) in members:
if member not in members2:
members2[member] = []
member.total = 0
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":
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)
class TokenForm(forms.Form):
token = forms.CharField(widget = forms.widgets.Textarea())
@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})
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()
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("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})
@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

@ -1,13 +1,17 @@
# coding: utf-8
from django.contrib import admin
from gestioncof.models import Survey, SurveyQuestion, SurveyQuestionAnswer
from gestioncof.models import Event, EventOption, EventOptionChoice
from gestioncof.models import CofProfile
from gestioncof.models import *
from gestioncof.petits_cours_models import *
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin
from django.core.urlresolvers import reverse
from django.utils.safestring import mark_safe
import django.forms as forms
#import eav.admin
from django.utils.translation import ugettext_lazy as _
from django.contrib.admin import SimpleListFilter
from django.db.models import Q
def add_link_field(target_model = '', field = '', link_text = unicode, desc_text = unicode):
def add_link(cls):
@ -61,36 +65,170 @@ class EventAdmin(admin.ModelAdmin):
EventOptionInline,
]
#from eav.forms import BaseDynamicEntityForm
#class CofProfileInline(eav.admin.BaseEntityInline, admin.StackedInline):
class CofProfileInline(admin.StackedInline):
model = CofProfile
#form = BaseDynamicEntityForm
inline_classes = ("collapse open",)
class UserProfileAdmin(UserAdmin):
def login_clipper(self, obj):
class FkeyLookup(object):
def __init__(self, fkeydecl, short_description=None, admin_order_field=None):
self.fk, fkattrs = fkeydecl.split('__', 1)
self.fkattrs = fkattrs.split('__')
self.short_description = short_description or self.fkattrs[-1]
self.admin_order_field = admin_order_field or fkeydecl
def __get__(self, obj, klass):
if obj is None:
return self # hack required to make Django validate (if obj is None, then we're a class, and classes are callable <wink>)
item = getattr(obj, self.fk)
for attr in self.fkattrs:
item = getattr(item, attr)
return item
def ProfileInfo(field, short_description, boolean = False):
def getter(self):
try:
return obj.get_profile().login_clipper
except UserProfile.DoesNotExist:
return getattr(self.get_profile(), field)
except CofProfile.DoesNotExist:
return ""
getter.short_description = short_description
getter.boolean = boolean
return getter
User.profile_login_clipper = FkeyLookup("profile__login_clipper", "Login clipper")
User.profile_num = FkeyLookup("profile__num", "Numéro")
User.profile_phone = ProfileInfo("phone", "Téléphone")
User.profile_occupation = ProfileInfo("occupation", "Occupation")
User.profile_departement = ProfileInfo("departement", "Departement")
User.profile_mailing_cof = ProfileInfo("mailing_cof", "ML COF", True)
User.profile_mailing_bda = ProfileInfo("mailing_bda", "ML BDA", True)
User.profile_mailing_bda_revente = ProfileInfo("mailing_bda_revente", "ML BDA-R", True)
class UserProfileAdmin(UserAdmin):
def is_buro(self, obj):
try:
return obj.get_profile().is_buro
except UserProfile.DoesNotExist:
except CofProfile.DoesNotExist:
return False
is_buro.short_description = 'Membre du Buro'
is_buro.boolean = True
def is_cof(self, obj):
try:
return obj.get_profile().is_cof
except UserProfile.DoesNotExist:
except CofProfile.DoesNotExist:
return False
is_cof.short_description = 'Membre du COF'
is_cof.boolean = True
list_display = UserAdmin.list_display + ('login_clipper','is_cof','is_buro',)
list_filter = UserAdmin.list_filter + ('profile__is_cof', 'profile__is_buro')
list_display = ('profile_num',) + UserAdmin.list_display + ('profile_login_clipper','profile_phone','profile_occupation','profile_mailing_cof','profile_mailing_bda','profile_mailing_bda_revente','is_cof','is_buro',)
list_display_links = ('username','email','first_name','last_name')
list_filter = UserAdmin.list_filter + ('profile__is_cof', 'profile__is_buro', 'profile__mailing_cof', 'profile__mailing_bda')
inlines = [
CofProfileInline,
]
import autocomplete_light
def user_unicode(self):
if self.first_name and self.last_name:
return u"%s %s (%s)" % (self.first_name, self.last_name, self.username)
else:
return self.username
User.__unicode__ = user_unicode
class EventRegistrationAdmin(admin.ModelAdmin):
form = autocomplete_light.modelform_factory(EventRegistration)
list_display = ('__unicode__','event','user','paid')
list_filter = ('paid',)
search_fields = ('user__username', 'user__first_name', 'user__last_name', 'user__email', 'event__title')
"""
class VoterProfileInlineForm(BaseDynamicEntityForm):
def __init__(self, data=None, *args, **kwargs):
super(BaseDynamicEntityForm, self).__init__(data, *args, **kwargs)
config_cls = self.instance._eav_config_cls
self.entity = getattr(self.instance, config_cls.eav_attr)
self.base_fields = {}
self._build_dynamic_fields()
class VoterProfileInline(eav.admin.BaseEntityInline, admin.StackedInline):
model = CofProfile
form = VoterProfileInlineForm
inline_classes = ("collapse open",)
fields = None
fieldsets = None
def get_fieldsets(self, request, obj=None):
formset = self.get_formset(request)
fk_name = self.fk_name or formset.fk.name
kw = {fk_name: obj} if obj else {}
instance = self.model(**kw)
form = formset.form(request.POST, instance=instance)
return [(None, {'fields': form.fields.keys()})]
class VotedListFilter(SimpleListFilter):
# Human-readable title which will be displayed in the
# right admin sidebar just above the filter options.
title = _(u'A voté')
# Parameter for the filter that will be used in the URL query.
parameter_name = 'voted'
def lookups(self, request, model_admin):
return (
('1', _('Yes')),
('0', _('No')),
)
def queryset(self, request, queryset):
# Returns the filtered queryset based on the value
# provided in the query string and retrievable via
# `self.value()`.
#
# Compare the requested value (either '80s' or '90s')
# to decide how to filter the queryset.
if self.value() == '1':
qs2 = User.objects.filter(profile__eav__a_vot = True)
return queryset.filter(pk__in = qs2.values_list('id', flat = True))
return voters
if self.value() == '0':
qs2 = User.objects.filter(profile__eav__a_vot = False)
return queryset.filter(pk__in = qs2.values_list('id', flat = True))
class VoterAdmin(UserProfileAdmin):
form = forms.ModelForm
fields = ('username','first_name','last_name')
readonly_fields = ('username','first_name','last_name')
fieldsets = None
def is_cof(self, obj):
try:
return obj.get_profile().is_cof
except CofProfile.DoesNotExist:
return False
is_cof.short_description = 'Membre du COF'
is_cof.boolean = True
def a_vote(self, obj):
try:
if not obj.get_profile().eav.a_vot:
return False
else:
return True
except CofProfile.DoesNotExist:
return False
a_vote.short_description = 'A voté'
a_vote.boolean = True
list_display = ('profile_num',) + UserAdmin.list_display + ('is_cof','a_vote')
list_filter = ('profile__is_cof', 'profile__is_buro', VotedListFilter)
inlines = [
VoterProfileInline,
]
"""
admin.site.register(Survey, SurveyAdmin)
admin.site.register(SurveyQuestion, SurveyQuestionAdmin)
admin.site.register(Event, EventAdmin)
@ -98,3 +236,8 @@ admin.site.register(EventOption, EventOptionAdmin)
admin.site.unregister(User)
admin.site.register(User, UserProfileAdmin)
admin.site.register(CofProfile)
admin.site.register(PetitCoursSubject)
admin.site.register(PetitCoursAbility)
admin.site.register(PetitCoursDemande)
#admin.site.register(Voter, VoterAdmin)
admin.site.register(EventRegistration, EventRegistrationAdmin)

View file

@ -7,8 +7,7 @@ def is_cof(user):
except:
return False
def cof_required(login_url = None):
return user_passes_test(lambda u: is_cof(u), login_url=login_url)
cof_required = user_passes_test(lambda u: is_cof(u))
def is_buro(user):
try:
@ -17,5 +16,4 @@ def is_buro(user):
except:
return False
def buro_required(login_url = None):
return user_passes_test(lambda u: is_buro(u), login_url=login_url)
buro_required = user_passes_test(lambda u: is_buro(u))

View file

@ -5,6 +5,8 @@ from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
from django.db.models.signals import post_save
from petits_cours_models import *
OCCUPATION_CHOICES = (
('exterieur', _(u"Extérieur")),
('1A', _(u"1A")),
@ -29,19 +31,23 @@ class CofProfile(models.Model):
user = models.OneToOneField(User, related_name = "profile")
login_clipper = models.CharField("Login clipper", max_length = 8, blank = True)
is_cof = models.BooleanField("Membre du COF", default = False)
num = models.IntegerField ("Numéro d'adhérent", blank = True, default = 0),
num = models.IntegerField ("Numéro d'adhérent", blank = True, default = 0)
phone = models.CharField("Téléphone", max_length = 20, blank = True)
occupation = models.CharField (_(u"Occupation"),
default = "1A",
choices = OCCUPATION_CHOICES,
max_length = choices_length (OCCUPATION_CHOICES))
departement = models.CharField (_(u"Département"), max_length = 50, blank = True)
type_cotiz = models.CharField (_(u"Type de cotisation"),
default = "normalien",
choices = TYPE_COTIZ_CHOICES,
max_length = choices_length (TYPE_COTIZ_CHOICES))
mailing_cof = models.BooleanField("Recevoir les mails COF", default = False)
mailing_bda_revente = models.BooleanField("Recevoir les mails de revente de places BDA", 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)
is_buro = models.BooleanField("Membre du Burô", default = False)
petits_cours_accept = models.BooleanField("Recevoir des petits cours", default = False)
petits_cours_sent = models.IntegerField("Nombre de propositions petits cours reçues", default = 0)
class Meta:
verbose_name = "Profil COF"
@ -62,6 +68,7 @@ class Event(models.Model):
end_date = models.DateField("Date de fin", blank = True, null = True)
description = models.TextField("Description", blank = True)
registration_open = models.BooleanField("Inscriptions ouvertes", default = True)
old = models.BooleanField("Archiver (événement fini)", default = False)
class Meta:
verbose_name = "Événement"
@ -100,10 +107,14 @@ class EventRegistration(models.Model):
verbose_name = "Inscription"
unique_together = ("user", "event")
def __unicode__(self):
return u"Inscription de %s à %s" % (unicode(self.user), unicode(self.event.title))
class Survey(models.Model):
title = models.CharField("Titre", max_length = 200)
details = models.TextField("Détails", blank = True)
survey_open = models.BooleanField("Sondage ouvert", default = True)
old = models.BooleanField("Archiver (sondage fini)", default = False)
class Meta:
verbose_name = "Sondage"
@ -140,3 +151,24 @@ class SurveyAnswer(models.Model):
class Meta:
verbose_name = "Réponses"
unique_together = ("user", "survey")
class Clipper(models.Model):
username = models.CharField("Identifiant", max_length = 20)
fullname = models.CharField("Nom complet", max_length = 200)
"""
class Voter(User):
class Meta:
proxy = True
verbose_name = "voteur"
def __unicode__(self):
return u"%s %s" % (self.first_name, self.last_name)
import eav
eav.register(CofProfile)
class ManagerOnlyEavConfig(eav.registry.EavConfig):
manager_only = True
eav.register(User, ManagerOnlyEavConfig)
"""

View file

@ -1,16 +1,10 @@
from django.contrib.sites.models import Site
from django.conf import settings
from django_cas.backends import CASBackend
from django.template import RequestContext, loader
from django.http import HttpResponse
from django.db import models, connection
from gestioncof.models import CofProfile
def render_page (request, data, template):
template = loader.get_template (template)
context = RequestContext (request, data)
return HttpResponse (template.render (context))
class COFCASBackend(CASBackend):
def authenticate(self, ticket, service):
"""Authenticates CAS ticket and retrieves user data"""
@ -39,3 +33,16 @@ def context_processor (request):
"site": Site.objects.get_current(),
}
return data
def lock_table(model):
cursor = connection.cursor()
table = model._meta.db_table
cursor.execute("LOCK TABLES %s WRITE" % table)
row = cursor.fetchone()
return row
def unlock_table(model):
cursor = connection.cursor()
cursor.execute("UNLOCK TABLES")
row = cursor.fetchone()
return row

View file

@ -1,7 +1,11 @@
from django import template
from django.utils.safestring import mark_safe
import re
register = template.Library()
@register.filter
def key(d, key_name):
try:
value = d[key_name]
@ -9,4 +13,24 @@ def key(d, key_name):
from django.conf import settings
value = settings.TEMPLATE_STRING_IF_INVALID
return value
key = register.filter('key', key)
def highlight_text(text, q):
q2 = "|".join(q.split())
pattern = re.compile(r"(?P<filter>%s)" % q2, re.IGNORECASE)
return mark_safe(re.sub(pattern, r"<span class='highlight'>\g<filter></span>", text))
@register.filter
def highlight_user(user, q):
if user.first_name and user.last_name:
text = u"%s %s (<tt>%s</tt>)" % (user.first_name, user.last_name, user.username)
else:
text = user.username
return highlight_text(text, q)
@register.filter
def highlight_clipper(clipper, q):
if clipper.fullname:
text = u"%s (<tt>%s</tt>)" % (clipper.fullname, clipper.username)
else:
text = clipper.username
return highlight_text(text, q)

View file

@ -1,29 +1,51 @@
# coding: utf-8
from django.shortcuts import redirect, get_object_or_404
from django.http import Http404
import unicodecsv
from django.shortcuts import redirect, get_object_or_404, render
from django.http import Http404, HttpResponse
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
from django import forms
from django.forms.widgets import RadioSelect, CheckboxSelectMultiple
from django.utils.translation import ugettext_lazy as _
from django.db.models import Max
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 CofProfile
from gestioncof.shared import render_page
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
@login_required
def home(request):
data = {"surveys": Survey.objects.filter(survey_open = True).all(),
"events": Event.objects.filter(registration_open = True).all()}
return render_page(request, data, "home.html")
data = {"surveys": Survey.objects.filter(old = False).all(),
"events": Event.objects.filter(old = False).all(),
"open_surveys": Survey.objects.filter(survey_open = True, old = False).all(),
"open_events": Event.objects.filter(registration_open = True, old = False).all()}
return render(request, "home.html", data)
def login(request):
if request.user.is_authenticated():
return redirect("gestioncof.views.home")
return render_page(request, {}, "login_switch.html")
return render(request, "login_switch.html", {})
def login_ext(request):
if request.method == "POST" and "username" in request.POST:
try:
user = User.objects.get(username = request.POST["username"])
if not user.has_usable_password() or user.password in ("","!"):
profile, created = CofProfile.objects.get_or_create(user = user)
if profile.login_clipper:
return render(request, "error.html", {"error_type": "use_clipper_login"})
else:
return render(request, "error.html", {"error_type": "no_password"})
except User.DoesNotExist:
pass
return django_login_view(request, template_name = 'login.html')
@login_required
def logout(request):
@ -124,11 +146,12 @@ def survey(request, survey_id):
except SurveyAnswer.DoesNotExist:
current_answer = None
form = SurveyForm(survey = survey)
return render_page(request, {"survey": survey, "form": form, "success": success, "deleted": deleted, "current_answer": current_answer}, "survey.html")
return render(request, "survey.html", {"survey": survey, "form": form, "success": success, "deleted": deleted, "current_answer": current_answer})
class EventForm(forms.Form):
def __init__(self, *args, **kwargs):
event = kwargs.pop("event")
self.event = event
current_choices = kwargs.pop("current_choices", None)
super(EventForm, self).__init__(*args, **kwargs)
choices = {}
@ -163,6 +186,25 @@ class EventForm(forms.Form):
if name.startswith('option_'):
yield (self.fields[name].option_id, value)
def get_event_form_choices(event, form):
all_choices = []
for option_id, choices_ids in form.choices():
option = get_object_or_404(EventOption, id = option_id,
event = event)
if type(choices_ids) != list:
choices_ids = [choices_ids]
if not option.multi_choices and len(choices_ids) > 1:
raise Http404
for choice_id in choices_ids:
if not choice_id:
continue
choice_id = int(choice_id)
choice = EventOptionChoice.objects.get(
id = choice_id,
event_option = option)
all_choices.append(choice)
return all_choices
@login_required
def event(request, event_id):
event = get_object_or_404(Event, id = event_id)
@ -172,27 +214,8 @@ def event(request, event_id):
if request.method == "POST":
form = EventForm(request.POST, event = event)
if form.is_valid():
all_choices = []
for option_id, choices_ids in form.choices():
option = get_object_or_404(EventOption, id = option_id,
event = event)
if type(choices_ids) != list:
choices_ids = [choices_ids]
if not option.multi_choices and len(choices_ids) > 1:
raise Http404
for choice_id in choices_ids:
if not choice_id:
continue
choice_id = int(choice_id)
choice = EventOptionChoice.objects.get(
id = choice_id,
event_option = option)
all_choices.append(choice)
try:
current_registration = EventRegistration.objects.get(user = request.user, event = event)
except EventRegistration.DoesNotExist:
current_registration = EventRegistration(user = request.user, event = event)
current_registration.save()
all_choices = get_event_form_choices(event, form)
(current_registration, _) = EventRegistration.objects.get_or_create(user = request.user, event = event)
current_registration.options = all_choices
current_registration.save()
success = True
@ -202,13 +225,7 @@ def event(request, event_id):
form = EventForm(event = event, current_choices = current_registration.options)
except EventRegistration.DoesNotExist:
form = EventForm(event = event)
return render_page(request, {"event": event, "form": form, "success": success}, "event.html")
@buro_required()
def event_status(request, event_id):
event = get_object_or_404(Event, id = event_id)
registrants = EventRegistration.objects.filter(event = event).all()
return render_page(request, {"event": event, "registrants": registrants}, "event_status.html")
return render(request, "event.html", {"event": event, "form": form, "success": success})
class SurveyStatusFilterForm(forms.Form):
def __init__(self, *args, **kwargs):
@ -240,7 +257,6 @@ class EventStatusFilterForm(forms.Form):
def __init__(self, *args, **kwargs):
event = kwargs.pop("event")
super(EventStatusFilterForm, self).__init__(*args, **kwargs)
choices = {}
for option in event.options.all():
for choice in option.choices.all():
name = "option_%d_choice_%d" % (option.id, choice.id)
@ -256,24 +272,35 @@ class EventStatusFilterForm(forms.Form):
field.option_id = option.id
field.choice_id = choice.id
self.fields[name] = field
# has_paid
name = "event_has_paid"
if self.is_bound and self.data.get(self.add_prefix(name), None):
initial = self.data.get(self.add_prefix(name), None)
else:
initial = "none"
field = forms.ChoiceField(label = "Événement payé",
choices = [("yes", "yes"),("no","no"),("none","none")],
widget = TriStateCheckbox,
required = False,
initial = initial)
self.fields[name] = field
def filters(self):
for name, value in self.cleaned_data.items():
if name.startswith('option_'):
yield (self.fields[name].option_id, self.fields[name].choice_id, value)
elif name == "event_has_paid":
yield ("has_paid", None, value)
def clean_post_for_status(initial):
d = dict(initial)
d = initial.copy()
for k, v in d.items():
if k.startswith("id_"):
del d[k]
if type(v) == list and len(v) >= 1:
d[k[3:]] = v[0]
else:
d[k[3:]] = v
d[k[3:]] = v
return d
@buro_required()
@buro_required
def event_status(request, event_id):
event = get_object_or_404(Event, id = event_id)
registrations_query = EventRegistration.objects.filter(event = event)
@ -281,6 +308,12 @@ def event_status(request, event_id):
form = EventStatusFilterForm(post_data or None, event = event)
if form.is_valid():
for option_id, choice_id, value in form.filters():
if option_id == "has_paid":
if value == "yes":
registrations_query = registrations_query.filter(paid = True)
elif value == "no":
registrations_query = registrations_query.filter(paid = False)
continue
choice = get_object_or_404(EventOptionChoice, id = choice_id, event_option__id = option_id)
if value == "none":
continue
@ -297,9 +330,9 @@ def event_status(request, event_id):
for user_choice in user_choices:
for choice in user_choice.options.all():
choices_count[choice.id] += 1
return render_page(request, {"event": event, "user_choices": user_choices, "options": options, "choices_count": choices_count, "form": form}, "event_status.html")
return render(request, "event_status.html", {"event": event, "user_choices": user_choices, "options": options, "choices_count": choices_count, "form": form})
@buro_required()
@buro_required
def survey_status(request, survey_id):
survey = get_object_or_404(Survey, id = survey_id)
answers_query = SurveyAnswer.objects.filter(survey = survey)
@ -323,7 +356,7 @@ def survey_status(request, survey_id):
for user_answer in user_answers:
for answer in user_answer.answers.all():
answers_count[answer.id] += 1
return render_page(request, {"survey": survey, "user_answers": user_answers, "questions": questions, "answers_count": answers_count, "form": form}, "survey_status.html")
return render(request, "survey_status.html", {"survey": survey, "user_answers": user_answers, "questions": questions, "answers_count": answers_count, "form": form})
class UserProfileForm(forms.ModelForm):
first_name = forms.CharField(label=_(u'Prénom'), max_length=30)
@ -339,6 +372,7 @@ class UserProfileForm(forms.ModelForm):
'last_name',
'phone',
'mailing_cof',
'mailing_bda',
'mailing_bda_revente',
]
@ -350,7 +384,7 @@ class UserProfileForm(forms.ModelForm):
class Meta:
model = CofProfile
fields = ("phone", "mailing_cof", "mailing_bda_revente",)
fields = ("phone", "mailing_cof", "mailing_bda", "mailing_bda_revente",)
@login_required
def profile(request):
@ -362,10 +396,312 @@ def profile(request):
success = True
else:
form = UserProfileForm(instance = request.user.get_profile())
return render_page(request, {"form": form, "success": success}, "profile.html")
return render(request, "profile.html", {"form": form, "success": success})
@login_required
class RegistrationUserForm(forms.ModelForm):
def __init__(self, *args, **kw):
super(RegistrationUserForm, self).__init__(*args, **kw)
self.fields['username'].help_text = ""
class Meta:
model = User
fields = ("username", "first_name", "last_name", "email")
class RegistrationProfileForm(forms.ModelForm):
def __init__(self, *args, **kw):
super(RegistrationProfileForm, self).__init__(*args, **kw)
self.fields['mailing_cof'].initial = True
self.fields['mailing_bda'].initial = True
self.fields['mailing_bda_revente'].initial = True
self.fields['num'].widget.attrs['readonly'] = True
self.fields.keyOrder = [
'login_clipper',
'phone',
'occupation',
'departement',
'is_cof',
'num',
'type_cotiz',
'mailing_cof',
'mailing_bda',
'mailing_bda_revente',
]
def save(self, *args, **kw):
instance = super(RegistrationProfileForm, self).save(*args, **kw)
if instance.is_cof and not instance.num:
# Generate new number
try:
lock_table(CofProfile)
aggregate = CofProfile.objects.aggregate(Max('num'))
instance.num = aggregate['num__max'] + 1
instance.save()
self.cleaned_data['num'] = instance.num
self.data['num'] = instance.num
finally:
unlock_table(CofProfile)
return instance
class Meta:
model = CofProfile
fields = ("login_clipper", "num", "phone", "occupation", "departement", "is_cof", "type_cotiz", "mailing_cof", "mailing_bda", "mailing_bda_revente",)
def registration_set_ro_fields(user_form, profile_form):
user_form.fields['username'].widget.attrs['readonly'] = True
profile_form.fields['login_clipper'].widget.attrs['readonly'] = True
@buro_required
def registration_form(request, login_clipper = None, username = None):
member = None
if login_clipper:
clipper = get_object_or_404(Clipper, username = login_clipper)
try: # check if the given user is already registered
member = User.objects.filter(username = login_clipper).get()
username = member.username
login_clipper = None
except User.DoesNotExist:
# new user, but prefill
user_form = RegistrationUserForm()
profile_form = RegistrationProfileForm()
user_form.fields['username'].initial = login_clipper
user_form.fields['email'].initial = login_clipper + "@clipper.ens.fr"
profile_form.fields['login_clipper'].initial = login_clipper
if clipper.fullname:
bits = clipper.fullname.split(" ")
user_form.fields['first_name'].initial = bits[0]
if len(bits) > 1:
user_form.fields['last_name'].initial = " ".join(bits[1:])
registration_set_ro_fields(user_form, profile_form)
if username:
member = get_object_or_404(User, username = username)
(profile, _) = CofProfile.objects.get_or_create(user = member)
# already existing, prefill
user_form = RegistrationUserForm(instance = member)
profile_form = RegistrationProfileForm(instance = profile)
registration_set_ro_fields(user_form, profile_form)
elif not login_clipper:
# new user
user_form = RegistrationUserForm()
profile_form = RegistrationProfileForm()
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é'),)
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)
paid = kwargs.pop("paid", None)
if paid == True:
kwargs["initial"] = {"status":"paid"}
elif paid == False:
kwargs["initial"] = {"status":"wait"}
else:
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)
all_choices = choices
for option in event.options.all():
choices = [(choice.id, choice.value) for choice in option.choices.all()]
if option.multi_choices:
initial = [] if option.id not in all_choices else all_choices[option.id]
field = forms.MultipleChoiceField(label = option.name,
choices = choices,
widget = CheckboxSelectMultiple,
required = False,
initial = initial)
else:
initial = None if option.id not in all_choices else all_choices[option.id][0]
field = forms.ChoiceField(label = option.name,
choices = choices,
widget = RadioSelect,
required = False,
initial = initial)
field.option_id = option.id
self.fields["option_%d" % option.id] = field
def choices(self):
for name, value in self.cleaned_data.items():
if name.startswith('option_'):
yield (self.fields[name].option_id, value)
@buro_required
def registration_form2(request, login_clipper = None, username = None):
events = Event.objects.filter(old = False).all()
member = None
if login_clipper:
clipper = get_object_or_404(Clipper, username = login_clipper)
try: # check if the given user is already registered
member = User.objects.filter(username = login_clipper).get()
username = member.username
login_clipper = None
except User.DoesNotExist:
# new user, but prefill
user_form = RegistrationUserForm()
profile_form = RegistrationProfileForm()
event_forms = [AdminEventForm(event = event) for event in events]
user_form.fields['username'].initial = login_clipper
user_form.fields['email'].initial = login_clipper + "@clipper.ens.fr"
profile_form.fields['login_clipper'].initial = login_clipper
if clipper.fullname:
bits = clipper.fullname.split(" ")
user_form.fields['first_name'].initial = bits[0]
if len(bits) > 1:
user_form.fields['last_name'].initial = " ".join(bits[1:])
registration_set_ro_fields(user_form, profile_form)
if username:
member = get_object_or_404(User, username = username)
(profile, _) = CofProfile.objects.get_or_create(user = member)
# already existing, prefill
user_form = RegistrationUserForm(instance = member)
profile_form = RegistrationProfileForm(instance = profile)
registration_set_ro_fields(user_form, profile_form)
event_forms = []
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)
except EventRegistration.DoesNotExist:
form = AdminEventForm(event = event)
event_forms.append(form)
elif not login_clipper:
# new user
user_form = RegistrationUserForm()
profile_form = RegistrationProfileForm()
event_forms = [AdminEventForm(event = event) for event in events]
return render(request, "registration_form.html", {"user_form": user_form, "profile_form": profile_form, "member": member, "login_clipper": login_clipper, "event_forms": event_forms})
@buro_required
def registration(request):
data = {"surveys": Survey.objects.filter(survey_open = True).all(),
"events": Event.objects.filter(registration_open = True).all()}
return render_page(request, data, "registration.html")
if request.POST:
request_dict = request.POST.copy()
if "num" in request_dict:
del request_dict["num"]
success = False
user_form = RegistrationUserForm(request_dict)
profile_form = RegistrationProfileForm(request_dict)
events = Event.objects.filter(old = False).all()
event_forms = [AdminEventForm(request_dict, event = event) for event in events]
user_form.is_valid()
profile_form.is_valid()
for event_form in event_forms: event_form.is_valid()
member = None
login_clipper = None
if "user_exists" in request_dict and request_dict["user_exists"]:
username = request_dict["username"]
try:
member = User.objects.filter(username = username).get()
(profile, _) = CofProfile.objects.get_or_create(user = member)
user_form = RegistrationUserForm(request_dict, instance = member)
profile_form = RegistrationProfileForm(request_dict, instance = profile)
except User.DoesNotExist:
try:
clipper = Clipper.objects.filter(username = username).get()
login_clipper = clipper.username
except Clipper.DoesNotExist:
pass
for form in event_forms:
if not form.is_valid(): break
if form.cleaned_data['status'] == 'no': continue
all_choices = get_event_form_choices(form.event, form)
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)
request_dict["num"] = profile.num
profile_form = RegistrationProfileForm(request_dict, instance = profile)
profile_form.is_valid()
profile_form.save()
for form in event_forms:
if form.cleaned_data['status'] == 'no':
try:
current_registration = EventRegistration.objects.get(user = member, event = form.event)
current_registration.delete()
except EventRegistration.DoesNotExist:
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.options = all_choices
current_registration.paid = (form.cleaned_data['status'] == 'paid')
current_registration.save()
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:
return render(request, "registration.html")
@buro_required
def export_members(request):
response = HttpResponse(mimetype = 'text/csv')
response['Content-Disposition'] = 'attachment; filename=membres_cof.csv'
writer = unicodecsv.UnicodeWriter(response)
for profile in CofProfile.objects.filter(is_cof = True).all():
user = profile.user
bits = [profile.num, user.username, user.first_name, user.last_name, user.email, profile.phone, profile.occupation, profile.departement, profile.type_cotiz]
writer.writerow([unicode(bit) for bit in bits])
return response
@buro_required
def export_mega_orgas(request):
response = HttpResponse(mimetype = 'text/csv')
response['Content-Disposition'] = 'attachment; filename=participants_mega.csv'
writer = unicodecsv.UnicodeWriter(response)
event = Event.objects.filter(title = "MEGA")
for reg in EventRegistration.objects.filter(event = event).exclude(options__id__exact = 3).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
@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.filter(title = "MEGA")
for reg in EventRegistration.objects.filter(event = event).filter(options__id__exact = 3).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
@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
@buro_required
def utile_cof(request):
return render(request, "utile_cof.html", {})
@buro_required
def utile_bda(request):
return render(request, "utile_bda.html", {})

18
manage.py Normal file → Executable file
View file

@ -1,14 +1,10 @@
#!/usr/bin/env python
from django.core.management import execute_manager
import imp
try:
imp.find_module('settings') # Assumed to be in the same directory.
except ImportError:
import sys
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n" % __file__)
sys.exit(1)
import settings
import os
import sys
if __name__ == "__main__":
execute_manager(settings)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cof.settings")
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)

View file

@ -133,6 +133,14 @@ form {
padding: 0;
}
#form-placeholder {
margin-top: 15px;
}
#form-placeholder form#profile {
margin-top: 10px;
}
form#profile table td, form#profile table th {
width: 400px;
}
@ -358,6 +366,15 @@ body {
font-weight: bold;
font-size: 18px;
margin-bottom: 5px;
float: left;
}
#main-login #header h1 {
float: none;
}
#header #homelink {
float: right;
}
tt {
@ -388,6 +405,10 @@ tt {
min-height: 2em;
}
input[disabled], input[readonly] {
color: #BBB;
}
input[type="text"], input[type=password] {
border: 1px solid #888;
-webkit-border-radius: 3px;
@ -397,6 +418,35 @@ input[type="text"], input[type=password] {
padding: 0px 2px 0px 2px;
}
input#search_autocomplete {
width: 600px;
font-size: 14px;
height: 20px;
padding: 10px 8px;
margin: 0 auto;
display: block;
color: #aaa;
}
input#search_autocomplete:focus {
font-size: 18px;
color: #343a4a;
}
.autocomplete {margin-bottom:5px;}
.yourlabs-autocomplete ul {list-style-type: none; padding: 0; margin: 0; font-size: 1.5em;}
.yourlabs-autocomplete li {padding: 6px 5px; display: block;}
.yourlabs-autocomplete li a {text-decoration: none;}
.yourlabs-autocomplete li a span.highlight {text-decoration: underline; font-weight: bold;}
.yourlabs-autocomplete li.autocomplete-header {background-color: #FFEF9E; padding: 3px 5px; color: #000;}
.yourlabs-autocomplete li.autocomplete-value {cursor: pointer;}
.yourlabs-autocomplete li.hilight {background-color: #EA4839; color: #FFF; }
.yourlabs-autocomplete li.hilight a {color: #FFF; }
.yourlabs-autocomplete { border: none; background: none; }
.yourlabs-autocomplete.outer-container { position: relative; }
.yourlabs-autocomplete.inner-container { position: absolute; left: -1px; top: 2px; border: 1px solid #B7B7B7; width: 618px; background: #fff; }
.yourlabs-autocomplete.inner-container { margin: 0; padding: 0; overflow-x: hidden; overflow-y: auto; position: relative; list-style-type: none; }
hr {
border: 0;
height: 1px;

View file

@ -1,172 +0,0 @@
# Django settings for cof project.
DEBUG = True
TEMPLATE_DEBUG = DEBUG
ADMINS = (
('Guillaume Seguin', 'guillaume@segu.in'),
)
MANAGERS = ADMINS
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
'NAME': 'cof_gestion', # Or path to database file if using sqlite3.
'USER': 'cof_gestion', # Not used with sqlite3.
'PASSWORD': '1OjKotIbmyro', # Not used with sqlite3.
'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
'PORT': '', # Set to empty string for default. Not used with sqlite3.
}
}
# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
# although not all choices may be available on all operating systems.
# On Unix systems, a value of None will cause Django to use the same
# timezone as the operating system.
# If running in a Windows environment this must be set to the same as your
# system time zone.
TIME_ZONE = 'Europe/Paris'
# Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = 'fr-fr'
SITE_ID = 1
# If you set this to False, Django will make some optimizations so as not
# to load the internationalization machinery.
USE_I18N = True
# If you set this to False, Django will not format dates, numbers and
# calendars according to the current locale
USE_L10N = True
# Absolute filesystem path to the directory that will hold user-uploaded files.
# Example: "/home/media/media.lawrence.com/media/"
MEDIA_ROOT = ''
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash.
# Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
MEDIA_URL = '/gestion/media/'
# Absolute path to the directory static files should be collected to.
# Don't put anything in this directory yourself; store your static files
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
# Example: "/home/media/media.lawrence.com/static/"
STATIC_ROOT = '/home/gestion/www/static'
# URL prefix for static files.
# Example: "http://media.lawrence.com/static/"
STATIC_URL = '/gestion/static/'
# URL prefix for admin static files -- CSS, JavaScript and images.
# Make sure to use a trailing slash.
# Examples: "http://foo.com/static/admin/", "/static/admin/".
ADMIN_MEDIA_PREFIX = '/gestion/static/grappelli/'
GRAPPELLI_ADMIN_HEADLINE = "GestioCOF"
GRAPPELLI_ADMIN_TITLE = "GestioCOF"
# Additional locations of static files
STATICFILES_DIRS = (
# Put strings here, like "/home/html/static" or "C:/www/django/static".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
)
# List of finder classes that know how to find static files in
# various locations.
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
# 'django.contrib.staticfiles.finders.DefaultStorageFinder',
)
# Make this unique, and don't share it with anybody.
SECRET_KEY = '1d%3zqeyj8dk^@afz9=q12gs&&5k@4qx)5%uc_(&%01)d&74af'
# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
# 'django.template.loaders.eggs.Loader',
)
TEMPLATE_CONTEXT_PROCESSORS = (
"django.contrib.auth.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
"django.core.context_processors.static",
"gestioncof.shared.context_processor",
)
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
# 'django_cas.middleware.CASMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
)
ROOT_URLCONF = 'urls'
TEMPLATE_DIRS = (
"/home/gestion/www/templates/gestioncof",
"/home/gestion/www/templates/bda",
)
LOGIN_URL = "/gestion/login"
LOGIN_REDIRECT_URL = "/gestion/"
CAS_SERVER_URL = 'https://cas.eleves.ens.fr/'
CAS_IGNORE_REFERER = True
CAS_REDIRECT_URL = '/gestion/'
CAS_EMAIL_FORMAT = "%s@clipper.ens.fr"
AUTH_PROFILE_MODULE = 'gestioncof.CofProfile'
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'gestioncof.shared.COFCASBackend',
)
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'grappelli',
'django.contrib.admin',
'django.contrib.admindocs',
'gestioncof',
'bda',
'pads',
'rezo',
)
# A sample logging configuration. The only tangible logging
# performed by this configuration is to send an email to
# the site admins on every HTTP 500 error.
# See http://docs.djangoproject.com/en/dev/topics/logging for
# more details on how to customize your logging configuration.
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler'
}
},
'loggers': {
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': True,
},
}
}

View file

@ -33,7 +33,7 @@ var django = {
}
deleteButtonHandler = function(elem) {
elem.bind("click", function() {
var deleteInput = $(this).prev(),
var deleteInput = $(this).prev().prev(),
form = $(this).parents(".dynamic-form").first();
// callback
// toggle options.predeleteCssClass and toggle checkbox
@ -95,7 +95,7 @@ var django = {
{% 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.views.inscription %}">
<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">
@ -105,6 +105,7 @@ var django = {
});
</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 />

View file

@ -2,7 +2,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" lang="fr">
<head>
<title>{{ site.name }}</title>
<link type="text/css" rel="stylesheet" href="{{ MEDIA_URL }}/cof.css" />
<link type="text/css" rel="stylesheet" href="{{ MEDIA_URL }}cof.css" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
{% block extra_head %}{% endblock %}
</head>

View file

@ -4,7 +4,9 @@
<div id="main-container">
<div id="main">
<div id="header">
<h1>{% block title %}<a href="{% url gestioncof.views.home %}">{{ site.name }}</a>{% endblock %}</h1>
<h1>{% block title %}<a href="{% url "gestioncof.views.home" %}">{{ site.name }}</a>{% endblock %}</h1>
{% block homelink %}<div id="homelink"><a href="{% url "gestioncof.views.home" %}">Retour à l'accueil</a></div>{% endblock %}
<div class="spacer"></div>
<hr />
</div>
<div id="main-content">

View file

@ -38,4 +38,8 @@
{% endif %}
{% endwith %}{% endfor %}
</ul>
<h3>Mailing list</h3>
<textarea style="width: 100%; height: 100px; margin-top: 10px;">
{% spaceless %}{% for user_choice in user_choices %}{% with user_choice.user as auser %}{% if user_choice.options.all %}{% if auser.email %}{{ auser.email }}{% else %}{{ auser.username }}{% endif %}, {% endif %}{% endwith %}{% endfor %}
{% endspaceless %}</textarea>
{% endblock %}

View file

@ -1,42 +1,53 @@
{% extends "base_title.html" %}
{% block homelink %}
{% endblock %}
{% block realcontent %}
<h2>Bienvenue, {% if user.first_name %}{{ user.first_name }}{% else %}<tt>{{ user.username }}</tt>{% endif %}</h2>
{% if events %}
{% if open_events %}
<h3>Événements</h3>
<ul>
{% for event in events %}
<li><a href="{% url gestioncof.views.event event.id %}">{{ event.title }}</a></li>
{% for event in open_events %}
<li><a href="{% url "gestioncof.views.event" event.id %}">{{ event.title }}</a></li>
{% endfor %}
</ul>
{% endif %}
{% if surveys %}
<h3>Sondages</h3>
{% if open_surveys %}
<h3>Sondages en cours</h3>
<ul>
{% for survey in surveys %}
<li><a href="{% url gestioncof.views.survey survey.id %}">{{ survey.title }}</a></li>
{% for survey in open_surveys %}
<li><a href="{% url "gestioncof.views.survey" survey.id %}">{{ survey.title }}</a></li>
{% endfor %}
</ul>
{% endif %}
<h3>Divers</h3>
<ul>
<!-- <li><a href="{% url "bda2.views.inscription" %}">Inscription au tirage au sort du BdA</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> -->
<li><a href="{% url "gestioncof.views.profile" %}">Éditer mon profil</a></li>
<li><a href="{% url "gestioncof.views.logout" %}">Se déconnecter</a></li>
</ul>
{% if user.get_profile.is_buro %}
<h3>Administration</h3>
<ul>
<li><a href="{% url "admin:index" %}">Administration générale</a></li>
<li><a href="{% url "gestioncof.views.registration" %}">Inscription d'un nouveau membre</a></li>
<br>
{% for event in events %}
<li><a href="{% url gestioncof.views.event_status event.id %}">Événement : {{ event.title }}</a></li>
<li><a href="{% url "gestioncof.views.event_status" event.id %}">Événement : {{ event.title }}</a></li>
{% endfor %}
{% for survey in surveys %}
<li><a href="{% url gestioncof.views.survey_status survey.id %}">Sondage : {{ survey.title }}</a></li>
<li><a href="{% url "gestioncof.views.survey_status" survey.id %}">Sondage : {{ survey.title }}</a></li>
{% endfor %}
<li><a href="{% url gestioncof.views.registration %}">Inscription d'un nouveau membre</a></li>
<li><a href="{% url admin:index %}">Administration</a></li>
<br>
<li><a href="{% url "gestioncof.views.utile_cof" %}">Liens utiles du COF</a></li>
<li><a href="{% url "gestioncof.views.utile_bda" %}">Liens utiles BdA</a></li>
</ul>
{% endif %}
<h3>Divers</h3>
<ul>
{% if user.get_profile.login_clipper == "seguin" or user.get_profile.login_clipper == "cmoreau" %}
<li><a href="{% url bda.views.inscription %}">Inscription au tirage au sort du BDA</a></li>
{% endif %}
<li><a href="{% url gestioncof.views.profile %}">Éditer mon profil</a></li>
<li><a href="{% url gestioncof.views.logout %}">Se déconnecter</a></li>
</ul>
<h3>Pour tout problème : cof@ens.fr.</h3>
{% endblock %}

View file

@ -10,7 +10,7 @@
<p class="error">Identifiants incorrects.</p>
{% endif %}
<form method="post" action="{% url django.contrib.auth.views.login %}">
<form method="post" action="{% url gestioncof.views.login_ext %}">
{% csrf_token %}
<table>
<tr>

View file

@ -6,7 +6,7 @@
<a id="login_clipper" href="{% url django_cas.views.login %}">
Compte clipper
</a>
<a id="login_outsider" href="{% url django.contrib.auth.views.login %}">
<a id="login_outsider" href="{% url gestioncof.views.login_ext %}">
Extérieur
</a>
<div class="spacer"></div>

View file

@ -1,15 +1,35 @@
{% extends "base_title.html" %}
{% block extra_head %}
<script src="{{ MEDIA_URL }}js/jquery.min.js" type="text/javascript"></script>
<script src="{{ STATIC_URL }}autocomplete_light/autocomplete.js" type="text/javascript"></script>
{% endblock %}
{% block realcontent %}
<h2>Inscription d'un nouveau membre</h2>
{% if success %}
<p class="success">{{ member.first_name }} {{ member.last_name }} a été inscrit avec succès en tant que membre n°{{ member.get_profile.num }}!</p>
{% endif %}
<form id="profile" method="post" action="{% url gestioncof.views.registration %}">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" class="btn-submit" value="Enregistrer l'inscription" />
</form>
<input type="text" name="q" id="search_autocomplete" spellcheck="false" />
<div id="form-placeholder"></div>
<script type="text/javascript">
$(document).ready(function() {
$('input#search_autocomplete').yourlabsAutocomplete({
url: '{% url gestioncof.autocomplete.autocomplete %}',
minimumCharacters: 1,
id: 'search_autocomplete',
choiceSelector: 'li:has(a)',
placeholder: "Chercher un utilisateur par nom, prénom ou identifiant clipper",
});
$('input#search_autocomplete').bind(
'selectChoice',
function(e, choice, autocomplete) {
autocomplete.hide();
link = choice.find('a:first');
if (link.length && link.attr('href') != undefined) {
$('#form-placeholder').html("").load(link.attr('href'), function() {
$('#form-placeholder').toggle().toggle();
});
}
}
);
});
</script>
{% endblock %}

View file

@ -37,5 +37,8 @@
</li>
{% endif %}
{% endwith %}{% endfor %}
</ul>
<h3>Mailing list</h3>
<textarea style="width: 100%; height: 100px; margin-top: 10px;">
{% spaceless %}{% for user_answer in user_answers %}{% with user_answer.user as auser %}{% if user_answer.answers.all %}{% if auser.email %}{{ auser.email }}{% else %}{{ auser.username }}{% endif %}, {% endif %}{% endwith %}{% endfor %}
{% endspaceless %}</textarea>
{% endblock %}

24
urls.py
View file

@ -1,24 +0,0 @@
from django.conf.urls.defaults import patterns, include, url
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
url(r'^$', 'gestioncof.views.home', name='home'),
url(r'^cas/login$', 'django_cas.views.login'),
url(r'^cas/logout$', 'django_cas.views.logout'),
url(r'^outsider/login$', 'django.contrib.auth.views.login', {'template_name': 'login.html'}),
url(r'^outsider/logout$', 'django.contrib.auth.views.logout', {'next_page': '/gestion/'}),
url(r'^login$', 'gestioncof.views.login'),
url(r'^logout$', 'gestioncof.views.logout'),
url(r'^profile$', 'gestioncof.views.profile'),
url(r'^registration$', 'gestioncof.views.registration'),
url(r'^bda/inscription$', 'bda.views.inscription'),
url(r'^survey/(?P<survey_id>\d+)$', 'gestioncof.views.survey'),
url(r'^event/(?P<event_id>\d+)$', 'gestioncof.views.event'),
url(r'^survey/(?P<survey_id>\d+)/status$', 'gestioncof.views.survey_status'),
url(r'^event/(?P<event_id>\d+)/status$', 'gestioncof.views.event_status'),
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
url(r'^admin/', include(admin.site.urls)),
url(r'^grappelli/', include('grappelli.urls')),
)