forked from DGNum/gestioCOF
Major update
This commit is contained in:
parent
8e1bf7b705
commit
2479b0a24d
33 changed files with 1194 additions and 110 deletions
0
bda/__init__.py
Normal file
0
bda/__init__.py
Normal file
14
bda/admin.py
Normal file
14
bda/admin.py
Normal 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
92
bda/algorithm.py
Normal 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
44
bda/models.py
Normal 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
16
bda/tests.py
Normal 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
40
bda/views.py
Normal 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")
|
|
@ -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
21
gestioncof/decorators.py
Normal 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)
|
|
@ -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")
|
||||||
|
|
|
@ -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):
|
||||||
|
|
0
gestioncof/templatetags/__init__.py
Normal file
0
gestioncof/templatetags/__init__.py
Normal file
12
gestioncof/templatetags/utils.py
Normal file
12
gestioncof/templatetags/utils.py
Normal 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)
|
|
@ -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
18
gestioncof/widgets.py
Normal 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))
|
234
media/cof.css
234
media/cof.css
|
@ -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
BIN
media/droidserif.woff
Normal file
Binary file not shown.
BIN
media/images/no.png
Normal file
BIN
media/images/no.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 686 B |
BIN
media/images/none.png
Normal file
BIN
media/images/none.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 576 B |
BIN
media/images/yes.png
Normal file
BIN
media/images/yes.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 610 B |
|
@ -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
|
||||||
|
|
115
templates/bda/inscription-bda.html
Normal file
115
templates/bda/inscription-bda.html
Normal 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'> </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œ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œu est ordonnée (du plus important au moins important), pour ajuster la priorité vous pouvez déplacer chaque vœu<br />
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
40
templates/bda/inscription-formset.html
Normal file
40
templates/bda/inscription-formset.html
Normal 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>
|
|
@ -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 %}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
16
templates/gestioncof/event.html
Normal file
16
templates/gestioncof/event.html
Normal 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 %}
|
41
templates/gestioncof/event_status.html
Normal file
41
templates/gestioncof/event_status.html
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
{% extends "base_title.html" %}
|
||||||
|
{% load utils %}
|
||||||
|
|
||||||
|
{% block realcontent %}
|
||||||
|
<h2>Événement: {{ event.title }}{% if user.is_staff %} – <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 %}
|
|
@ -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 %}
|
||||||
|
|
15
templates/gestioncof/profile.html
Normal file
15
templates/gestioncof/profile.html
Normal 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 %}
|
15
templates/gestioncof/registration.html
Normal file
15
templates/gestioncof/registration.html
Normal 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 %}
|
|
@ -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 %}
|
||||||
|
|
41
templates/gestioncof/survey_status.html
Normal file
41
templates/gestioncof/survey_status.html
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
{% extends "base_title.html" %}
|
||||||
|
{% load utils %}
|
||||||
|
|
||||||
|
{% block realcontent %}
|
||||||
|
<h2>Sondage: {{ survey.title }}{% if user.is_staff %} – <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 %}
|
69
templates/gestioncof/tristate_js.html
Normal file
69
templates/gestioncof/tristate_js.html
Normal 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>
|
6
urls.py
6
urls.py
|
@ -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')),
|
||||||
|
|
Loading…
Reference in a new issue