Major update

This commit is contained in:
root 2012-07-11 17:39:20 +02:00
parent 8e1bf7b705
commit 2479b0a24d
33 changed files with 1194 additions and 110 deletions

0
bda/__init__.py Normal file
View file

14
bda/admin.py Normal file
View file

@ -0,0 +1,14 @@
# coding: utf-8
from django.contrib import admin
from bda.models import Spectacle, Participant, ChoixSpectacle
class ChoixSpectacleInline(admin.TabularInline):
model = ChoixSpectacle
sortable_field_name = "priority"
class ParticipantAdmin(admin.ModelAdmin):
inlines = [ChoixSpectacleInline]
admin.site.register(Spectacle)
admin.site.register(Participant, ParticipantAdmin)

92
bda/algorithm.py Normal file
View file

@ -0,0 +1,92 @@
# coding: utf-8
from django.conf import settings
import random
class Algorithm(object):
shows = None
ranks = None
origranks = None
double = None
def __init__(self, shows, members):
"""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.shows = []
showdict = {}
for show in shows:
showdict[show] = show
show.requests = []
self.shows.append(show)
self.ranks = {}
self.origranks = {}
self.double = {}
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
self.ranks[member] = ranks
self.double[member] = double
self.origranks[member] = dict(ranks)
def IncrementRanks(self, member, currank, increment = 1):
for show in self.ranks[member]:
if self.ranks[member][show] > currank:
self.ranks[member][show] -= increment
def appendResult(self, l, member, show):
l.append((member,
self.ranks[member][show],
self.origranks[member][show],
self.double[member][show]))
def __call__(self, seed):
random.seed(seed)
results = []
for show in self.shows:
# On regroupe tous les gens ayant le même rang
groups = {}
for i in range(1, settings.NUM_CHOICES + 1):
groups[i] = []
for member in show.requests:
groups[self.ranks[member][show]].append(member)
# On passe à l'attribution
winners = []
losers = []
for i in range(1, settings.NUM_CHOICES + 1):
group = list(groups[i])
random.shuffle(group)
for member in group:
if self.double[member][show]: # 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:
self.appendResult(winners, member, show)
self.appendResult(losers, member, show)
else:
self.appendResult(losers, member, show)
self.appendResult(losers, member, show)
self.IncrementRanks(member, i, 2)
else: # simple
if len(winners) < show.slots:
self.appendResult(winners, member, show)
else:
self.appendResult(losers, member, show)
self.IncrementRanks(member, i)
results.append((show,winners,losers))
return results

44
bda/models.py Normal file
View file

@ -0,0 +1,44 @@
# coding: utf-8
from django.db import models
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
from django.db.models.signals import post_save
class 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)
description = models.TextField ("Description", blank = True)
slots_description = models.TextField ("Description des places", blank = True)
slots = models.IntegerField ("Places")
priority = models.IntegerField ("Priorité", default = 1000)
class Meta:
verbose_name = "Spectacle"
ordering = ("priority", "date","title",)
def __repr__ (self):
return u"[%s]" % self.__unicode__()
def __unicode__ (self):
return u"%s - %s @ %s" % (self.title, self.date, self.location)
class Participant (models.Model):
user = models.ForeignKey(User, unique = True)
choices = models.ManyToManyField(Spectacle, through = "ChoixSpectacle")
def __unicode__ (self):
return u"%s" % (self.user)
class ChoixSpectacle (models.Model):
participant = models.ForeignKey(Participant)
spectacle = models.ForeignKey(Spectacle, related_name = "participants")
priority = models.PositiveIntegerField("Priorité")
double = models.BooleanField("Deux places<sup>1</sup>")
autoquit = models.BooleanField("Abandon<sup>2</sup>")
class Meta:
ordering = ("priority",)
#unique_together = (("participant", "spectacle",),)
verbose_name = "voeu"

16
bda/tests.py Normal file
View file

@ -0,0 +1,16 @@
"""
This file demonstrates writing tests using the unittest module. These will pass
when you run "manage.py test".
Replace this with more appropriate tests for your application.
"""
from django.test import TestCase
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.assertEqual(1 + 1, 2)

40
bda/views.py Normal file
View file

@ -0,0 +1,40 @@
# coding: utf-8
from django.contrib.auth.decorators import login_required
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
class BaseBdaFormSet(BaseInlineFormSet):
def clean(self):
"""Checks that no two articles have the same title."""
if any(self.errors):
# Don't bother validating the formset unless each form is valid on its own
return
spectacles = []
for i in range(0, self.total_form_count()):
form = self.forms[i]
if not form.cleaned_data:
continue
spectacle = form.cleaned_data['spectacle']
delete = form.cleaned_data['DELETE']
if not delete and spectacle in spectacles:
raise forms.ValidationError("Vous ne pouvez pas vous inscrire deux fois pour le même spectacle.")
spectacles.append(spectacle)
@login_required
def inscription(request):
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():
formset.save()
success = True
formset = BdaFormSet(instance = participant)
else:
formset = BdaFormSet(instance = participant)
return render_page(request, {"formset": formset, "success": success}, "inscription-bda.html")

View file

@ -3,6 +3,9 @@
from django.contrib import admin from django.contrib import admin
from gestioncof.models import Survey, SurveyQuestion, SurveyQuestionAnswer from gestioncof.models import Survey, SurveyQuestion, SurveyQuestionAnswer
from gestioncof.models import Event, EventOption, EventOptionChoice from gestioncof.models import Event, EventOption, EventOptionChoice
from gestioncof.models import CofProfile
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
@ -41,12 +44,11 @@ class SurveyAdmin(admin.ModelAdmin):
SurveyQuestionInline, SurveyQuestionInline,
] ]
@add_link_field() class EventOptionChoiceInline(admin.TabularInline):
class EventOptionChoiceInline(admin.StackedInline):
model = EventOptionChoice model = EventOptionChoice
@add_link_field() @add_link_field(desc_text = lambda x: "Choix", link_text = lambda x: "Éditer les choix")
class EventOptionInline(admin.StackedInline): class EventOptionInline(admin.TabularInline):
model = EventOption model = EventOption
class EventOptionAdmin(admin.ModelAdmin): class EventOptionAdmin(admin.ModelAdmin):
@ -59,7 +61,40 @@ class EventAdmin(admin.ModelAdmin):
EventOptionInline, EventOptionInline,
] ]
class CofProfileInline(admin.StackedInline):
model = CofProfile
inline_classes = ("collapse open",)
class UserProfileAdmin(UserAdmin):
def login_clipper(self, obj):
try:
return obj.get_profile().login_clipper
except UserProfile.DoesNotExist:
return ""
def is_buro(self, obj):
try:
return obj.get_profile().is_buro
except UserProfile.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:
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')
inlines = [
CofProfileInline,
]
admin.site.register(Survey, SurveyAdmin) admin.site.register(Survey, SurveyAdmin)
admin.site.register(SurveyQuestion, SurveyQuestionAdmin) admin.site.register(SurveyQuestion, SurveyQuestionAdmin)
admin.site.register(Event, EventAdmin) admin.site.register(Event, EventAdmin)
admin.site.register(EventOption, EventOptionAdmin) admin.site.register(EventOption, EventOptionAdmin)
admin.site.unregister(User)
admin.site.register(User, UserProfileAdmin)
admin.site.register(CofProfile)

21
gestioncof/decorators.py Normal file
View file

@ -0,0 +1,21 @@
from django_cas.decorators import user_passes_test
def is_cof(user):
try:
profile = user.get_profile()
return profile.is_cof
except:
return False
def cof_required(login_url = None):
return user_passes_test(lambda u: is_cof(u), login_url=login_url)
def is_buro(user):
try:
profile = user.get_profile()
return profile.is_buro
except:
return False
def buro_required(login_url = None):
return user_passes_test(lambda u: is_buro(u), login_url=login_url)

View file

@ -26,9 +26,11 @@ def choices_length (choices):
return reduce (lambda m, choice: max (m, len (choice[0])), choices, 0) return reduce (lambda m, choice: max (m, len (choice[0])), choices, 0)
class CofProfile(models.Model): class CofProfile(models.Model):
user = models.OneToOneField(User) user = models.OneToOneField(User, related_name = "profile")
login_clipper = models.CharField("Login clipper", max_length = 8) login_clipper = models.CharField("Login clipper", max_length = 8, blank = True)
is_cof = models.BooleanField("Membre du COF", default = False) is_cof = models.BooleanField("Membre du COF", default = False)
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"), occupation = models.CharField (_(u"Occupation"),
default = "1A", default = "1A",
choices = OCCUPATION_CHOICES, choices = OCCUPATION_CHOICES,
@ -37,20 +39,27 @@ class CofProfile(models.Model):
default = "normalien", default = "normalien",
choices = TYPE_COTIZ_CHOICES, choices = TYPE_COTIZ_CHOICES,
max_length = choices_length (TYPE_COTIZ_CHOICES)) max_length = choices_length (TYPE_COTIZ_CHOICES))
mailing_cof = models.BooleanField("Recevoir les mails COF", default = True) mailing_cof = models.BooleanField("Recevoir les mails COF", default = False)
mailing_bda_revente = models.BooleanField("Recevoir les mails de revente de places BDA", default = True) mailing_bda_revente = models.BooleanField("Recevoir les mails de revente de places BDA", default = False)
is_buro = models.BooleanField("Membre du Burô", default = False) is_buro = models.BooleanField("Membre du Burô", default = False)
class Meta:
verbose_name = "Profil COF"
verbose_name_plural = "Profils COF"
def __unicode__(self):
return unicode(self.user.username)
def create_user_profile(sender, instance, created, **kwargs): def create_user_profile(sender, instance, created, **kwargs):
if created: if created:
CofProfile.objects.create(user = instance) CofProfile.objects.get_or_create(user = instance)
post_save.connect(create_user_profile, sender = User) post_save.connect(create_user_profile, sender = User)
class Event(models.Model): class Event(models.Model):
title = models.CharField("Titre", max_length = 200) title = models.CharField("Titre", max_length = 200)
location = models.CharField("Lieu", max_length = 200) location = models.CharField("Lieu", max_length = 200)
start_date = models.DateField("Date de début", blank = True) start_date = models.DateField("Date de début", blank = True, null = True)
end_date = models.DateField("Date de fin", blank = True) end_date = models.DateField("Date de fin", blank = True, null = True)
description = models.TextField("Description", blank = True) description = models.TextField("Description", blank = True)
registration_open = models.BooleanField("Inscriptions ouvertes", default = True) registration_open = models.BooleanField("Inscriptions ouvertes", default = True)
@ -61,8 +70,9 @@ class Event(models.Model):
return unicode(self.title) return unicode(self.title)
class EventOption(models.Model): class EventOption(models.Model):
event = models.ForeignKey(Event) event = models.ForeignKey(Event, related_name = "options")
name = models.CharField("Option", max_length = 200) name = models.CharField("Option", max_length = 200)
multi_choices = models.BooleanField("Choix multiples", default = False)
class Meta: class Meta:
verbose_name = "Option" verbose_name = "Option"
@ -71,7 +81,7 @@ class EventOption(models.Model):
return unicode(self.name) return unicode(self.name)
class EventOptionChoice(models.Model): class EventOptionChoice(models.Model):
event_option = models.ForeignKey(EventOption) event_option = models.ForeignKey(EventOption, related_name = "choices")
value = models.CharField("Valeur", max_length = 200) value = models.CharField("Valeur", max_length = 200)
class Meta: class Meta:
@ -88,6 +98,7 @@ class EventRegistration(models.Model):
class Meta: class Meta:
verbose_name = "Inscription" verbose_name = "Inscription"
unique_together = ("user", "event")
class Survey(models.Model): class Survey(models.Model):
title = models.CharField("Titre", max_length = 200) title = models.CharField("Titre", max_length = 200)
@ -124,7 +135,8 @@ class SurveyQuestionAnswer(models.Model):
class SurveyAnswer(models.Model): class SurveyAnswer(models.Model):
user = models.ForeignKey(User) user = models.ForeignKey(User)
survey = models.ForeignKey(Survey) survey = models.ForeignKey(Survey)
answers = models.ManyToManyField(SurveyQuestionAnswer) answers = models.ManyToManyField(SurveyQuestionAnswer, related_name = "selected_by")
class Meta: class Meta:
verbose_name = "Réponses" verbose_name = "Réponses"
unique_together = ("user", "survey")

View file

@ -1,18 +1,35 @@
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.conf import settings from django.conf import settings
from django_cas.backends import CASBackend from django_cas.backends import CASBackend
from django.template import RequestContext, loader
from django.http import HttpResponse
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): class COFCASBackend(CASBackend):
def authenticate(self, ticket, service): def authenticate(self, ticket, service):
"""Authenticates CAS ticket and retrieves user data""" """Authenticates CAS ticket and retrieves user data"""
user = super(COFCASBackend, self).authenticate(ticket, service) user = super(COFCASBackend, self).authenticate(ticket, service)
profile = user.get_profile() try:
profile = user.get_profile()
except CofProfile.DoesNotExist:
profile, created = CofProfile.objects.get_or_create(user = user)
profile.save()
if not profile.login_clipper: if not profile.login_clipper:
profile.login_clipper = user.username profile.login_clipper = user.username
profile.save() profile.save()
if not user.email: if not user.email:
user.email = settings.CAS_EMAIL_FORMAT % profile.login_clipper user.email = settings.CAS_EMAIL_FORMAT % profile.login_clipper
user.save() user.save()
if profile.is_buro and not user.is_superuser:
user.is_superuser = True
user.is_staff = True
user.save()
return user return user
def context_processor (request): def context_processor (request):

View file

View file

@ -0,0 +1,12 @@
from django import template
register = template.Library()
def key(d, key_name):
try:
value = d[key_name]
except KeyError:
from django.conf import settings
value = settings.TEMPLATE_STRING_IF_INVALID
return value
key = register.filter('key', key)

View file

@ -1,18 +1,18 @@
# coding: utf-8
from django.shortcuts import redirect, get_object_or_404 from django.shortcuts import redirect, get_object_or_404
from django.template import RequestContext, loader from django.http import Http404
from django.http import HttpResponse, HttpResponseRedirect, Http404
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django import forms from django import forms
from django.forms.widgets import RadioSelect, CheckboxSelectMultiple from django.forms.widgets import RadioSelect, CheckboxSelectMultiple
from django.utils.translation import ugettext_lazy as _
from gestioncof.models import Survey, SurveyQuestion, SurveyQuestionAnswer, SurveyAnswer from gestioncof.models import Survey, SurveyQuestion, SurveyQuestionAnswer, SurveyAnswer
from gestioncof.models import Event, EventOption, EventOptionChoice, EventRegistration from gestioncof.models import Event, EventOption, EventOptionChoice, EventRegistration
from gestioncof.models import CofProfile
def render_page (request, data, template): from gestioncof.shared import render_page
template = loader.get_template (template) from gestioncof.decorators import buro_required, cof_required
context = RequestContext (request, data) from gestioncof.widgets import TriStateCheckbox
return HttpResponse (template.render (context))
@login_required @login_required
def home(request): def home(request):
@ -27,7 +27,11 @@ def login(request):
@login_required @login_required
def logout(request): def logout(request):
if request.user.get_profile().login_clipper: try:
profile = request.user.get_profile()
except CofProfile.DoesNotExist:
profile, created = CofProfile.objects.get_or_create(user = request.user)
if profile.login_clipper:
return redirect("django_cas.views.logout") return redirect("django_cas.views.logout")
else: else:
return redirect("django.contrib.auth.views.logout") return redirect("django.contrib.auth.views.logout")
@ -74,35 +78,294 @@ def survey(request, survey_id):
if not survey.survey_open: if not survey.survey_open:
raise Http404 raise Http404
success = False success = False
deleted = False
if request.method == "POST": if request.method == "POST":
form = SurveyForm(request.POST, survey = survey) form = SurveyForm(request.POST, survey = survey)
if form.is_valid(): if request.POST.get('delete'):
all_answers = []
for question_id, answers_ids in form.answers():
question = get_object_or_404(SurveyQuestion, id = question_id,
survey = survey)
if type(answers_ids) != list:
answers_ids = [answers_ids]
if not question.multi_answers and len(answers_ids) > 1:
raise Http404
for answer_id in answers_ids:
answer_id = int(answer_id)
answer = SurveyQuestionAnswer.objects.get(
id = answer_id,
survey_question = question)
all_answers.append(answer)
try: try:
current_answer = SurveyAnswer.objects.get(user = request.user, survey = survey) current_answer = SurveyAnswer.objects.get(user = request.user, survey = survey)
current_answer.delete()
current_answer = None
except SurveyAnswer.DoesNotExist: except SurveyAnswer.DoesNotExist:
current_answer = SurveyAnswer(user = request.user, survey = survey) current_answer = None
current_answer.save() form = SurveyForm(survey = survey)
current_answer.answers = all_answers
current_answer.save()
success = True success = True
deleted = True
else:
if form.is_valid():
all_answers = []
for question_id, answers_ids in form.answers():
question = get_object_or_404(SurveyQuestion, id = question_id,
survey = survey)
if type(answers_ids) != list:
answers_ids = [answers_ids]
if not question.multi_answers and len(answers_ids) > 1:
raise Http404
for answer_id in answers_ids:
if not answer_id:
continue
answer_id = int(answer_id)
answer = SurveyQuestionAnswer.objects.get(
id = answer_id,
survey_question = question)
all_answers.append(answer)
try:
current_answer = SurveyAnswer.objects.get(user = request.user, survey = survey)
except SurveyAnswer.DoesNotExist:
current_answer = SurveyAnswer(user = request.user, survey = survey)
current_answer.save()
current_answer.answers = all_answers
current_answer.save()
success = True
else: else:
try: try:
current_answer = SurveyAnswer.objects.get(user = request.user, survey = survey) current_answer = SurveyAnswer.objects.get(user = request.user, survey = survey)
form = SurveyForm(survey = survey, current_answers = current_answer.answers) form = SurveyForm(survey = survey, current_answers = current_answer.answers)
except SurveyAnswer.DoesNotExist: except SurveyAnswer.DoesNotExist:
current_answer = None
form = SurveyForm(survey = survey) form = SurveyForm(survey = survey)
return render_page(request, {"survey": survey, "form": form, "success": success}, "survey.html") return render_page(request, {"survey": survey, "form": form, "success": success, "deleted": deleted, "current_answer": current_answer}, "survey.html")
class EventForm(forms.Form):
def __init__(self, *args, **kwargs):
event = kwargs.pop("event")
current_choices = kwargs.pop("current_choices", None)
super(EventForm, 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)
@login_required
def event(request, event_id):
event = get_object_or_404(Event, id = event_id)
if not event.registration_open:
raise Http404
success = False
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()
current_registration.options = all_choices
current_registration.save()
success = True
else:
try:
current_registration = EventRegistration.objects.get(user = request.user, event = event)
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")
class SurveyStatusFilterForm(forms.Form):
def __init__(self, *args, **kwargs):
survey = kwargs.pop("survey")
super(SurveyStatusFilterForm, self).__init__(*args, **kwargs)
answers = {}
for question in survey.questions.all():
for answer in question.answers.all():
name = "question_%d_answer_%d" % (question.id, answer.id)
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 = "%s : %s" % (question.question, answer.answer),
choices = [("yes", "yes"),("no","no"),("none","none")],
widget = TriStateCheckbox,
required = False,
initial = initial)
field.question_id = question.id
field.answer_id = answer.id
self.fields[name] = field
def filters(self):
for name, value in self.cleaned_data.items():
if name.startswith('question_'):
yield (self.fields[name].question_id, self.fields[name].answer_id, value)
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)
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 = "%s : %s" % (option.name, choice.value),
choices = [("yes", "yes"),("no","no"),("none","none")],
widget = TriStateCheckbox,
required = False,
initial = initial)
field.option_id = option.id
field.choice_id = choice.id
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)
def clean_post_for_status(initial):
d = dict(initial)
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
return d
@buro_required()
def event_status(request, event_id):
event = get_object_or_404(Event, id = event_id)
registrations_query = EventRegistration.objects.filter(event = event)
post_data = clean_post_for_status(request.POST)
form = EventStatusFilterForm(post_data or None, event = event)
if form.is_valid():
for option_id, choice_id, value in form.filters():
choice = get_object_or_404(EventOptionChoice, id = choice_id, event_option__id = option_id)
if value == "none":
continue
if value == "yes":
registrations_query = registrations_query.filter(options__id__exact = choice.id)
elif value == "no":
registrations_query = registrations_query.exclude(options__id__exact = choice.id)
user_choices = registrations_query.prefetch_related("user").all()
options = EventOption.objects.filter(event = event).all()
choices_count = {}
for option in options:
for choice in option.choices.all():
choices_count[choice.id] = 0
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")
@buro_required()
def survey_status(request, survey_id):
survey = get_object_or_404(Survey, id = survey_id)
answers_query = SurveyAnswer.objects.filter(survey = survey)
post_data = clean_post_for_status(request.POST)
form = SurveyStatusFilterForm(post_data or None, survey = survey)
if form.is_valid():
for question_id, answer_id, value in form.filters():
answer = get_object_or_404(SurveyQuestionAnswer, id = answer_id, survey_question__id = question_id)
if value == "none":
continue
if value == "yes":
answers_query = answers_query.filter(answers__id__exact = answer.id)
elif value == "no":
answers_query = answers_query.exclude(answers__id__exact = answer.id)
user_answers = answers_query.prefetch_related("user").all()
questions = SurveyQuestion.objects.filter(survey = survey).all()
answers_count = {}
for question in questions:
for answer in question.answers.all():
answers_count[answer.id] = 0
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")
class UserProfileForm(forms.ModelForm):
first_name = forms.CharField(label=_(u'Prénom'), max_length=30)
last_name = forms.CharField(label=_(u'Nom'), max_length=30)
def __init__(self, *args, **kw):
super(UserProfileForm, self).__init__(*args, **kw)
self.fields['first_name'].initial = self.instance.user.first_name
self.fields['last_name'].initial = self.instance.user.last_name
self.fields.keyOrder = [
'first_name',
'last_name',
'phone',
'mailing_cof',
'mailing_bda_revente',
]
def save(self, *args, **kw):
super(UserProfileForm, self).save(*args, **kw)
self.instance.user.first_name = self.cleaned_data.get('first_name')
self.instance.user.last_name = self.cleaned_data.get('last_name')
self.instance.user.save()
class Meta:
model = CofProfile
fields = ("phone", "mailing_cof", "mailing_bda_revente",)
@login_required
def profile(request):
success = False
if request.method == "POST":
form = UserProfileForm(request.POST, instance = request.user.get_profile())
if form.is_valid():
form.save()
success = True
else:
form = UserProfileForm(instance = request.user.get_profile())
return render_page(request, {"form": form, "success": success}, "profile.html")
@login_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")

18
gestioncof/widgets.py Normal file
View file

@ -0,0 +1,18 @@
from django.forms.widgets import Widget
from django.forms.util import flatatt
from django.utils.safestring import mark_safe
class TriStateCheckbox(Widget):
def __init__(self, attrs=None, choices=()):
super(TriStateCheckbox, self).__init__(attrs)
# choices can be any iterable, but we may need to render this widget
# multiple times. Thus, collapse it into a list so it can be consumed
# more than once.
self.choices = list(choices)
def render(self, name, value, attrs=None, choices=()):
if value is None: value = 'none'
final_attrs = self.build_attrs(attrs, value=value)
output = [u"<span class=\"tristate\"%s></span>" % flatatt(final_attrs)]
return mark_safe('\n'.join(output))

View file

@ -12,23 +12,136 @@ html,body {
clear: both; clear: both;
} }
#cof a:link, #cof a:active, #cof a:visited { a:link, a:active, a:visited {
color: #111; color: #111;
background: transparent; background: transparent;
} }
#cof a:hover { a:hover {
color: #444; color: #444;
background: transparent; background: transparent;
} }
#cof form { form#profile table {
display: block; border-collapse: collapse;
padding: 0; }
width: 100%;
}
#cof fieldset { table#bda_formset {
width: 100%;
}
.bda-field-spectacle {
width: 65%;
}
.bda-field-spectacle select {
width: 94%;
margin: 0 3%;
}
.bda-field-double, .bda-field-autoquit, .bda-field-priority {
text-align: center;
}
.bda-field-double {
width: 15%;
}
.bda-field-autoquit {
width: 15%;
}
.tools-cell {
width: 32px;
}
.tools {
width: 32px;
}
.tools a.icon.drag-handler, .tools a.icon.delete-handler {
float: left;
}
.tools a.icon.drag-handler {
margin-right: 5px;
}
form#bda_form p {
font-size: 0.8em;
}
table#bda_formset {
border-spacing: 0px 5px;
}
tbody.bda_formset_content {
}
tr.dynamic-form td {
border-width: 1px 1px 1px 0px;
border-style: solid;
border-color: #888;
background: #EEE;
}
tr.dynamic-form td:first-child {
border-left-width: 1px;
}
tr.dynamic-form.predelete td {
background-color: #FFECEC;
border-width: 1px 1px 1px 0px;
border-style: solid;
border-color: #CCC;
}
tr.dynamic-form.predelete td:first-child {
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
border: 1px solid #CCC;
}
tr.dynamic-form.predelete td:last-child {
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
}
.ui-sortable-helper {
}
tr.ui-sortable-placeholder td, .placeholder-cell {
background: transparent;
border-width: 1px 1px 1px 0px;
border-style: solid;
border-color: #CCC;
}
tr.ui-sortable-placeholder td:first-child {
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
border: 1px solid #CCC;
}
tr.ui-sortable-placeholder td:last-child {
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
}
form {
padding: 0;
}
form#profile table td, form#profile table th {
width: 400px;
}
form#profile table th {
text-align: right;
}
fieldset {
border: 0; border: 0;
margin: 0; margin: 0;
padding: 0; padding: 0;
@ -37,16 +150,16 @@ html,body {
width: auto; width: auto;
} }
#cof fieldset legend { fieldset legend {
display: none; display: none;
} }
#cof #main-login-container { #main-login-container {
width: 500px; width: 500px;
margin: 7em auto; margin: 7em auto;
} }
#cof #main-login { #main-login {
width: 500px; width: 500px;
border: 15px solid #333; border: 15px solid #333;
-webkit-border-radius: 20px; -webkit-border-radius: 20px;
@ -54,13 +167,14 @@ html,body {
border-radius: 20px; border-radius: 20px;
} }
#cof #main-container { #main-container {
max-width: 90%;
width: 800px; width: 800px;
margin: 7em auto; margin: 7em auto;
display: block;
} }
#cof #main { #main {
width: 800px;
border: 15px solid #333; border: 15px solid #333;
-webkit-border-radius: 20px; -webkit-border-radius: 20px;
-moz-border-radius: 20px; -moz-border-radius: 20px;
@ -69,61 +183,72 @@ html,body {
box-shadow: 0 0 100px #AAA inset; box-shadow: 0 0 100px #AAA inset;
} }
#cof #main #main-content { #main #main-content {
font-size: 1.25em; font-size: 1.25em;
margin-top: 10px; margin-top: 10px;
} }
#cof #main #main-content ul { #main #main-content ul {
line-height: 1.3em; line-height: 1.3em;
} }
#cof #main h1 { #main h1 {
font-size: 3em; font-size: 3em;
} }
#cof #main h1 a { #main h1 a {
color: #333; color: #333;
text-decoration: none; text-decoration: none;
} }
#cof #main h2 { #main h2 {
font-size: 1.5em; font-size: 1.5em;
margin-bottom: 10px; margin-bottom: 10px;
} }
#cof #main h3 { #main h3 {
font-size: 1.3em; font-size: 1.3em;
margin-top: 5px;
} }
#cof #main p { #main h4 {
margin-top: 10px;
font-weight: bold;
}
#main p {
margin-top: 8px; margin-top: 8px;
margin-bottom: 8px; margin-bottom: 8px;
} }
#cof #main form li { #main form li {
list-style: none; list-style: none;
} }
#cof .success { .success {
font-weight: bold; font-weight: bold;
color: #00B000; color: #00B000;
background-color: transparent; background-color: transparent;
} }
#cof #main form ul.errorlist li { #main form ul.errorlist li {
font-weight: bold; font-weight: bold;
color: #B00000; color: #B00000;
background-color: transparent; background-color: transparent;
list-style: disc; list-style: disc;
} }
#cof #main-login.login_block { form#bda_form ul.errorlist li {
padding-left: 0px;
list-style: none;
}
#main-login.login_block {
padding: 2em; padding: 2em;
box-shadow: 0 0 100px #AAA inset; box-shadow: 0 0 100px #AAA inset;
} }
#cof a#login_clipper, #cof a#login_outsider { a#login_clipper, a#login_outsider {
float: left; float: left;
display: block; display: block;
width: 250px; width: 250px;
@ -137,41 +262,41 @@ html,body {
color: #FFF; color: #FFF;
} }
#cof a#login_clipper { a#login_clipper {
background-color: #123E96; background-color: #123E96;
box-shadow: 0 0 100px #040C78 inset; box-shadow: 0 0 100px #040C78 inset;
} }
#cof a#login_clipper:hover { a#login_clipper:hover {
background-color: #164BB6; background-color: #164BB6;
} }
#cof a#login_outsider { a#login_outsider {
background-color: #961221; background-color: #961221;
box-shadow: 0 0 100px #780411 inset; box-shadow: 0 0 100px #780411 inset;
} }
#cof a#login_outsider:hover { a#login_outsider:hover {
background-color: #B31729; background-color: #B31729;
} }
#cof #main-login label { #main-login label {
font-size: 11px; font-size: 11px;
} }
#cof #main-login label span.accesskey { #main-login label span.accesskey {
text-decoration: underline; text-decoration: underline;
} }
#cof #main-login input { #main-login input {
letter-spacing: 1px; letter-spacing: 1px;
} }
#cof #main-login .btn-row { #main-login .btn-row {
float: right; float: right;
} }
#cof .btn-submit { .btn-submit, .btn-addmore {
float: none; float: none;
clear: none; clear: none;
display: inline; display: inline;
@ -186,7 +311,7 @@ html,body {
background: linear-gradient(center top, #EEE, #CCC); background: linear-gradient(center top, #EEE, #CCC);
} }
#cof #main-login .btn-reset { #main-login .btn-reset {
float: none; float: none;
clear: none; clear: none;
margin-left: 5px; margin-left: 5px;
@ -200,7 +325,7 @@ html,body {
/* RESET --------------------------------- */ /* RESET --------------------------------- */
/* reset some properties for elements since defaults are not crossbrowser - http: //meyerweb.com/eric/thoughts/2007/05/01/reset-reloaded/ */ /* reset some properties for elements since defaults are not crossbrowser - http: //meyerweb.com/eric/thoughts/2007/05/01/reset-reloaded/ */
html,body,div,span,h1,h2,h3,p,a,img,ul,li,fieldset,form,label,legend { html,body,div,span,h1,h2,h3,h4,p,a,img,ul,li,fieldset,form,label,legend {
margin: 0; margin: 0;
padding: 0; padding: 0;
border: 0; border: 0;
@ -245,7 +370,7 @@ tt {
} }
/* FORMS --------------------------------- */ /* FORMS --------------------------------- */
input { #main-login input {
border-width: 1px; border-width: 1px;
font-family: Verdana,sans-serif; font-family: Verdana,sans-serif;
font-size: 1.1em; font-size: 1.1em;
@ -254,7 +379,7 @@ input {
min-height: 1.5em; min-height: 1.5em;
} }
input[type="text"], input[type=password] { #main-login input[type="text"], #main-login input[type=password] {
border: 2px solid #888; border: 2px solid #888;
-webkit-border-radius: 8px; -webkit-border-radius: 8px;
-moz-border-radius: 8px; -moz-border-radius: 8px;
@ -263,6 +388,15 @@ input[type="text"], input[type=password] {
min-height: 2em; min-height: 2em;
} }
input[type="text"], input[type=password] {
border: 1px solid #888;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
box-shadow: 0 0 3px #AAA inset;
padding: 0px 2px 0px 2px;
}
hr { hr {
border: 0; border: 0;
height: 1px; height: 1px;
@ -275,24 +409,6 @@ hr {
background: linear-gradient(left, hsla(0,0%,70%,0) 0%, hsla(0,0%,70%,.75) 50%, hsla(0,0%,70%,0) 100%); background: linear-gradient(left, hsla(0,0%,70%,0) 0%, hsla(0,0%,70%,.75) 50%, hsla(0,0%,70%,0) 100%);
} }
.fm-v div.row { .tristate:hover {
margin: 0; cursor: pointer;
padding: .5em 0;
width: 100%;
}
.fm-v div.row label {
float: left;
width: 100%;
line-height: 1.5;
}
.fm-v div.row input.btn-submit {
display: block;
margin: 0;
}
.fm-v div.row.fl-controls-left {
width: 50%;
float: left;
} }

BIN
media/droidserif.woff Normal file

Binary file not shown.

BIN
media/images/no.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 686 B

BIN
media/images/none.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

BIN
media/images/yes.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 B

View file

@ -95,7 +95,7 @@ TEMPLATE_LOADERS = (
) )
TEMPLATE_CONTEXT_PROCESSORS = ( TEMPLATE_CONTEXT_PROCESSORS = (
"django.core.context_processors.auth", "django.contrib.auth.context_processors.auth",
"django.core.context_processors.debug", "django.core.context_processors.debug",
"django.core.context_processors.i18n", "django.core.context_processors.i18n",
"django.core.context_processors.media", "django.core.context_processors.media",
@ -116,6 +116,7 @@ ROOT_URLCONF = 'urls'
TEMPLATE_DIRS = ( TEMPLATE_DIRS = (
"/home/gestion/www/templates/gestioncof", "/home/gestion/www/templates/gestioncof",
"/home/gestion/www/templates/bda",
) )
LOGIN_URL = "/gestion/login" LOGIN_URL = "/gestion/login"
@ -142,6 +143,9 @@ INSTALLED_APPS = (
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.admindocs', 'django.contrib.admindocs',
'gestioncof', 'gestioncof',
'bda',
'pads',
'rezo',
) )
# A sample logging configuration. The only tangible logging # A sample logging configuration. The only tangible logging

View file

@ -0,0 +1,115 @@
{% extends "base_title.html" %}
{% block extra_head %}
<link href="{{ STATIC_URL }}grappelli/jquery/ui/css/custom-theme/jquery-ui-1.8.custom.css" rel="stylesheet" type="text/css" media="screen" title="no title" charset="utf-8" />
<script src="{{ STATIC_URL }}grappelli/jquery/jquery-1.6.2.min.js" type="text/javascript"></script>
<script src="{{ STATIC_URL }}grappelli/jquery/ui/js/jquery-ui-1.8.15.custom.min.js" type="text/javascript"></script>
<link href="{{ STATIC_URL }}grappelli/css/tools.css" rel="stylesheet" type="text/css" />
<link href="{{ STATIC_URL }}grappelli/css/jquery-ui-grappelli-extensions.css" rel="stylesheet" type="text/css" />
{% endblock %}
{% block realcontent %}
<script type="text/javascript">
var django = {
"jQuery": jQuery.noConflict(true)
};
(function($) {
cloneMore = function(selector, type) {
var newElement = $(selector).clone(true);
var total = $('#id_' + type + '-TOTAL_FORMS').val();
newElement.find(':input').each(function() {
var name = $(this).attr('name').replace('-' + (total-1) + '-','-' + total + '-');
var id = 'id_' + name;
$(this).attr({'name': name, 'id': id}).val('').removeAttr('checked');
});
newElement.find('label').each(function() {
var newFor = $(this).attr('for').replace('-' + (total-1) + '-','-' + total + '-');
$(this).attr('for', newFor);
});
total++;
$('#id_' + type + '-TOTAL_FORMS').val(total);
$(selector).after(newElement);
}
deleteButtonHandler = function(elem) {
elem.bind("click", function() {
var deleteInput = $(this).prev(),
form = $(this).parents(".dynamic-form").first();
// callback
// toggle options.predeleteCssClass and toggle checkbox
if (form.hasClass("has_original")) {
form.toggleClass("predelete");
if (deleteInput.attr("checked")) {
deleteInput.attr("checked", false);
} else {
deleteInput.attr("checked", true);
}
}
// callback
});
};
$(document).ready(function($) {
deleteButtonHandler($("table#bda_formset tbody.bda_formset_content").find("a.delete-handler"));
$("table#bda_formset tbody.bda_formset_content").sortable({
handle: "a.drag-handler",
items: "tr",
axis: "y",
appendTo: 'body',
forceHelperSize: true,
placeholder: 'ui-sortable-placeholder',
forcePlaceholderSize: true,
containment: 'form#bda_form',
tolerance: 'pointer',
start: function(evt, ui) {
var template = "",
len = ui.item.children("td").length;
for (var i = 0; i < len; i++) {
template += "<td style='height:" + (ui.item.outerHeight() + 12 ) + "px' class='placeholder-cell'>&nbsp;</td>"
}
template += "";
ui.placeholder.html(template);
},
stop: function(evt, ui) {
// Toggle div.table twice to remove webkits border-spacing bug
$("table#bda_formset").toggle().toggle();
},
});
$("#bda_form").bind("submit", function(){
var sortable_field_name = "priority";
var i = 1;
$(".bda_formset_content").find("tr").each(function(){
var fields = $(this).find("td :input[value]"),
select = $(this).find("td select");
if (select.val() && fields.serialize()) {
$(this).find("input[name$='"+sortable_field_name+"']").val(i);
i++;
}
});
});
});
})(django.jQuery);
</script>
<h2>Inscription au tirage au sort du BDA</h2>
{% if success %}
<p class="success">Votre inscription a été mise à jour avec succès !</p>
{% endif %}
<form id="bda_form" method="post" action="{% url bda.views.inscription %}">
{% csrf_token %}
{% include "inscription-formset.html" %}
<input type="button" class="btn-addmore" value="Ajouter un autre v&oelig;u" id="add_more">
<script>
django.jQuery('#add_more').click(function() {
cloneMore('tbody.bda_formset_content tr:last-child', 'choixspectacle_set');
});
</script>
<input type="submit" class="btn-submit" value="Enregistrer" />
<hr />
<p class="footnotes">
<sup>1</sup>: demander deux places pour ce spectable<br />
<sup>2</sup>: abandonner une place si impossible d'en obtenir une seconde pour ce spectacle (si vous avez coché l'option <tt>Deux places</tt> pour ce spectacle)<br />
<sup>3</sup>: cette liste de v&oelig;u est ordonnée (du plus important au moins important), pour ajuster la priorité vous pouvez déplacer chaque v&oelig;u<br />
</p>
</form>
{% endblock %}

View file

@ -0,0 +1,40 @@
{{ formset.non_form_errors.as_ul }}
<table id="bda_formset" class="form">
{{ formset.management_form }}
{% for form in formset.forms %}
{% if forloop.first %}
<thead><tr>
{% for field in form.visible_fields %}
{% if field.name != "DELETE" and field.name != "priority" %}
<th class="bda-field-{{ field.name }}">{{ field.label|safe|capfirst }}</th>
{% endif %}
{% endfor %}
<th><sup>3</sup></th>
</tr></thead>
<tbody class="bda_formset_content">
{% endif %}
<tr class="{% cycle row1,row2 %} dynamic-form {% if form.instance.pk %}has_original{% endif %}">
{% for field in form.visible_fields %}
{% if field.name != "DELETE" and field.name != "priority" %}
<td class="bda-field-{{ field.name }}">
{% if forloop.first %}
{{ form.non_field_errors }}
{% for hidden in form.hidden_fields %}{{ hidden }}{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endif %}
{% endfor %}
<td class="tools-cell"><div class="tools">
<a href="javascript://" class="icon drag-handler" title="Déplacer"></a>
<input type="checkbox" name="{{ form.DELETE.html_name }}" style="display: none;" />
<input type="hidden" name="{{ form.priority.html_name }}" style="{{ form.priority.value }}" />
<a href="javascript://" class="icon delete-handler" title="Supprimer"></a>
</div>
<div class="spacer"></div>
</td>
</tr>
{% endfor %}
</tbody>
</table>

View file

@ -4,6 +4,7 @@
<title>{{ site.name }}</title> <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" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
{% block extra_head %}{% endblock %}
</head> </head>
<body id="cof"> <body id="cof">
{% block content %}{% endblock %} {% block content %}{% endblock %}

View file

@ -6,9 +6,9 @@
<div id="header"> <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>
<hr /> <hr />
<div id="main-content"> </div>
{% block realcontent %}{% endblock %} <div id="main-content">
</div> {% block realcontent %}{% endblock %}
</div> </div>
</div> </div>
</div> </div>

View file

@ -0,0 +1,16 @@
{% extends "base_title.html" %}
{% block realcontent %}
<h2>Événement: {{ event.title }}</h2>
{% if success %}
<p class="success">Votre inscription a bien été enregistrée ! Vous pouvez cependant la modifier jusqu'à la fin des inscriptions.</p>
{% endif %}
{% if event.details %}
<p>{{ event.details }}</p>
{% endif %}
<form method="post" action="{% url gestioncof.views.event event.id %}">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" class="btn-submit" value="Enregistrer" />
</form>
{% endblock %}

View file

@ -0,0 +1,41 @@
{% extends "base_title.html" %}
{% load utils %}
{% block realcontent %}
<h2>Événement: {{ event.title }}{% if user.is_staff %} &ndash; <a href="{% url admin:gestioncof_event_change event.id %}">Administration</a>{% endif %}</h2>
{% if event.details %}
<p>{{ event.details }}</p>
{% endif %}
{% include "tristate_js.html" %}
<h3>Filtres</h3>
<form method="post" action="{% url gestioncof.views.event_status event.id %}">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" class="btn-submit" value="Filtrer" />
</form>
<h3>Résultats globaux</h3>
{% for option in options %}
<h4>{{ option.value }}</h4>
<ul>
{% for choice in option.choices.all %}
<li>{{ choice.value }} : {{ choices_count|key:choice.id }}</li>
{% endfor %}
</ul>
{% endfor %}
<h3>Réponses individuelles</h3>
<ul>
{% for user_choice in user_choices %}{% with user_choice.user as auser %}
{% if user_choice.options.all %}
<li>
{% if auser.first_name and auser.last_name %}{{ auser.first_name }} {{ auser.last_name }}
{% else %}<tt>{{ auser.username }}</tt>{% endif %} :
<ul>
{% for choice in user_choice.options.all %}
<li>{{ choice.event_option.name }} : {{ choice.value }}</li>
{% endfor %}
</ul>
</li>
{% endif %}
{% endwith %}{% endfor %}
</ul>
{% endblock %}

View file

@ -18,11 +18,25 @@
{% endfor %} {% endfor %}
</ul> </ul>
{% endif %} {% endif %}
{% if user.get_profile.is_buro %}
<h3>Administration</h3>
<ul>
{% for event in events %}
<li><a href="{% url gestioncof.views.event_status event.id %}">Événement : {{ event.title }}</a></li>
{% endfor %}
{% for survey in surveys %}
<li><a href="{% url gestioncof.views.survey_status survey.id %}">Sondage : {{ survey.title }}</a></li>
{% endfor %}
<li><a href="{% url gestioncof.views.registration %}">Inscription d'un nouveau membre</a></li>
<li><a href="{% url admin:index %}">Administration</a></li>
</ul>
{% endif %}
<h3>Divers</h3> <h3>Divers</h3>
<ul> <ul>
{% if user.is_buro or user.is_staff %} {% if user.get_profile.login_clipper == "seguin" or user.get_profile.login_clipper == "cmoreau" %}
<li><a href="{% url admin:index %}">Administration</a></li> <li><a href="{% url bda.views.inscription %}">Inscription au tirage au sort du BDA</a></li>
{% endif %} {% 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> <li><a href="{% url gestioncof.views.logout %}">Se déconnecter</a></li>
</ul> </ul>
{% endblock %} {% endblock %}

View file

@ -0,0 +1,15 @@
{% extends "base_title.html" %}
{% block realcontent %}
<h2>Modifier mon profil</h2>
{% if success %}
<p class="success">Votre profil a été mis à jour avec succès !</p>
{% endif %}
<form id="profile" method="post" action="{% url gestioncof.views.profile %}">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" class="btn-submit" value="Enregistrer" />
</form>
{% endblock %}

View file

@ -0,0 +1,15 @@
{% extends "base_title.html" %}
{% 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>
{% endblock %}

View file

@ -3,8 +3,12 @@
{% block realcontent %} {% block realcontent %}
<h2>Sondage: {{ survey.title }}</h2> <h2>Sondage: {{ survey.title }}</h2>
{% if success %} {% if success %}
{% if deleted %}
<p class="success">Votre réponse a bien été supprimée !</p>
{% else %}
<p class="success">Votre réponse a bien été enregistrée ! Vous pouvez cependant la modifier jusqu'à la fin du sondage.</p> <p class="success">Votre réponse a bien été enregistrée ! Vous pouvez cependant la modifier jusqu'à la fin du sondage.</p>
{% endif %} {% endif %}
{% endif %}
{% if survey.details %} {% if survey.details %}
<p>{{ survey.details }}</p> <p>{{ survey.details }}</p>
{% endif %} {% endif %}
@ -12,5 +16,8 @@
{% csrf_token %} {% csrf_token %}
{{ form.as_p }} {{ form.as_p }}
<input type="submit" class="btn-submit" value="Enregistrer" /> <input type="submit" class="btn-submit" value="Enregistrer" />
{% if current_answer %}
<input type="submit" name="delete" class="btn-submit" value="Supprimer ma réponse" />
{% endif %}
</form> </form>
{% endblock %} {% endblock %}

View file

@ -0,0 +1,41 @@
{% extends "base_title.html" %}
{% load utils %}
{% block realcontent %}
<h2>Sondage: {{ survey.title }}{% if user.is_staff %} &ndash; <a href="{% url admin:gestioncof_survey_change survey.id %}">Administration</a>{% endif %}</h2>
{% if survey.details %}
<p>{{ survey.details }}</p>
{% endif %}
<h3>Filtres</h3>
{% include "tristate_js.html" %}
<form method="post" action="{% url gestioncof.views.survey_status survey.id %}">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" class="btn-submit" value="Filtrer" />
</form>
<h3>Résultats globaux</h3>
{% for question in questions %}
<h4>{{ question.question }}</h4>
<ul>
{% for answer in question.answers.all %}
<li>{{ answer.answer }} : {{ answers_count|key:answer.id }}</li>
{% endfor %}
</ul>
{% endfor %}
<h3>Réponses individuelles</h3>
<ul>
{% for user_answer in user_answers %}{% with user_answer.user as auser %}
{% if user_answer.answers.all %}
<li>
{% if auser.first_name and auser.last_name %}{{ auser.first_name }} {{ auser.last_name }}
{% else %}<tt>{{ auser.username }}</tt>{% endif %} :
<ul>
{% for answer in user_answer.answers.all %}
<li>{{ answer.survey_question.question }} : {{ answer.answer }}</li>
{% endfor %}
</ul>
</li>
{% endif %}
{% endwith %}{% endfor %}
</ul>
{% endblock %}

View file

@ -0,0 +1,69 @@
<script type="text/javascript">
var supernifty_tristate = function() {
var
YES = { image: "{{ MEDIA_URL }}/images/yes.png", state: "yes" },
NO = { image: "{{ MEDIA_URL }}/images/no.png", state: "no" },
NONE = { image: "{{ MEDIA_URL }}/images/none.png", state: "none" };
function tristate_elements() {
if ( document.getElementsByClassName != undefined ) {
return document.getElementsByClassName( "tristate" );
}
else {
var
all = document.getElementsByTagName('*'),
alllength = all.length,
result = [], i;
for ( i = 0; i < alllength; i++ ) {
if ( all[i].className == 'tristate' ) {
result.push( all[i] );
}
}
return result;
}
}
return {
init: function() {
var list = tristate_elements(),
i,
html;
for ( i = 0; i < list.length; i++ ) {
var state = NONE;
var value = list[i].getAttribute("value");
if ( value == 'yes' )
state = YES;
else if ( value == 'no' )
state = NO;
html = "<img id=\"" + list[i].id + "_img\" src=\"" + state.image + "\" onclick=\"supernifty_tristate.update('" + list[i].id + "')\"/><input type=\"hidden\" id=\"" + list[i].id + "_frm\" name=\"" + list[i].id + "\" value=\"" + state.state + "\"/>";
list[i].innerHTML = html;
}
},
update: function(id) {
var state = document.getElementById( id + "_frm" ).value, next;
// yes -> no -> none -> yes
if ( state == 'yes' ) {
next = NO;
}
else if ( state == 'no' ) {
next = NONE;
}
else { // assume none
next = YES;
}
document.getElementById( id + "_img" ).src = next.image;
document.getElementById( id + "_frm" ).value = next.state;
}
}
}();
// onload handler
var existing_onload = window.onload;
window.onload = function() {
if ( existing_onload != undefined ) {
existing_onload();
}
supernifty_tristate.init();
}
</script>

View file

@ -10,7 +10,13 @@ urlpatterns = patterns('',
url(r'^outsider/logout$', 'django.contrib.auth.views.logout', {'next_page': '/gestion/'}), url(r'^outsider/logout$', 'django.contrib.auth.views.logout', {'next_page': '/gestion/'}),
url(r'^login$', 'gestioncof.views.login'), url(r'^login$', 'gestioncof.views.login'),
url(r'^logout$', 'gestioncof.views.logout'), 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'^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/doc/', include('django.contrib.admindocs.urls')),
url(r'^admin/', include(admin.site.urls)), url(r'^admin/', include(admin.site.urls)),
url(r'^grappelli/', include('grappelli.urls')), url(r'^grappelli/', include('grappelli.urls')),