Merge branch 'master' into k-fet

This commit is contained in:
Martin Pépin 2016-08-31 23:52:31 +02:00
commit 3d25d1ab77
25 changed files with 650 additions and 199 deletions

View file

@ -152,7 +152,7 @@ Il ne vous reste plus qu'à initialiser les modèles de Django avec la commande
Une base de donnée pré-remplie est disponible en lançant la commande : Une base de donnée pré-remplie est disponible en lançant la commande :
python manage.py loaddata users bda gestion python manage.py loaddata users root bda gestion sites
Vous êtes prêts à développer ! Lancer GestioCOF en faisant Vous êtes prêts à développer ! Lancer GestioCOF en faisant
@ -171,6 +171,6 @@ Pour mettre à jour les modèles après une migration, il faut ensuite faire :
## Documentation utilisateur ## Documentation utilisateur
Une brève documentation utilisateur pour se faliliariser plus vite avec l'outil Une brève documentation utilisateur pour se familiariser plus vite avec l'outil
est accessible sur le est accessible sur le
[wiki](https://git.eleves.ens.fr/cof-geek/gestioCOF/wikis/home). [wiki](https://git.eleves.ens.fr/cof-geek/gestioCOF/wikis/home).

View file

@ -9,7 +9,7 @@ from django.core.mail import send_mail
from django.contrib import admin from django.contrib import admin
from django.db.models import Sum, Count from django.db.models import Sum, Count
from bda.models import Spectacle, Salle, Participant, ChoixSpectacle,\ from bda.models import Spectacle, Salle, Participant, ChoixSpectacle,\
Attribution, Tirage Attribution, Tirage, Quote, CategorieSpectacle
from django import forms from django import forms
from datetime import timedelta from datetime import timedelta
@ -182,7 +182,12 @@ class ChoixSpectacleAdmin(admin.ModelAdmin):
'spectacle__title') 'spectacle__title')
class QuoteInline(admin.TabularInline):
model = Quote
class SpectacleAdmin(admin.ModelAdmin): class SpectacleAdmin(admin.ModelAdmin):
inlines = [QuoteInline]
model = Spectacle model = Spectacle
list_display = ("title", "date", "tirage", "location", "slots", "price", list_display = ("title", "date", "tirage", "location", "slots", "price",
"listing") "listing")
@ -194,7 +199,7 @@ class SpectacleAdmin(admin.ModelAdmin):
class TirageAdmin(admin.ModelAdmin): class TirageAdmin(admin.ModelAdmin):
model = Tirage model = Tirage
list_display = ("title", "ouverture", "fermeture", "active", list_display = ("title", "ouverture", "fermeture", "active",
"enable_do_tirage") "enable_do_tirage")
readonly_fields = ("tokens", ) readonly_fields = ("tokens", )
list_filter = ("active", ) list_filter = ("active", )
search_fields = ("title", ) search_fields = ("title", )
@ -205,6 +210,7 @@ class SalleAdmin(admin.ModelAdmin):
search_fields = ('name', 'address') search_fields = ('name', 'address')
admin.site.register(CategorieSpectacle)
admin.site.register(Spectacle, SpectacleAdmin) admin.site.register(Spectacle, SpectacleAdmin)
admin.site.register(Salle, SalleAdmin) admin.site.register(Salle, SalleAdmin)
admin.site.register(Participant, ParticipantAdmin) admin.site.register(Participant, ParticipantAdmin)

View file

@ -74,7 +74,6 @@
"description": "Jazz / Funk", "description": "Jazz / Funk",
"title": "Un super concert", "title": "Un super concert",
"price": 10.0, "price": 10.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 2, "location": 2,
"date": "2016-09-30T18:00:00Z", "date": "2016-09-30T18:00:00Z",
@ -91,7 +90,6 @@
"description": "Homemade", "description": "Homemade",
"title": "Une super pi\u00e8ce", "title": "Une super pi\u00e8ce",
"price": 10.0, "price": 10.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 3, "location": 3,
"date": "2016-09-29T14:00:00Z", "date": "2016-09-29T14:00:00Z",
@ -108,7 +106,6 @@
"description": "Plein air, soleil, bonne musique", "description": "Plein air, soleil, bonne musique",
"title": "Concert pour la f\u00eate de la musique", "title": "Concert pour la f\u00eate de la musique",
"price": 5.0, "price": 5.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 1, "location": 1,
"date": "2016-09-21T15:00:00Z", "date": "2016-09-21T15:00:00Z",
@ -125,7 +122,6 @@
"description": "Sous le regard s\u00e9v\u00e8re de Louis Pasteur", "description": "Sous le regard s\u00e9v\u00e8re de Louis Pasteur",
"title": "Op\u00e9ra sans d\u00e9cors", "title": "Op\u00e9ra sans d\u00e9cors",
"price": 5.0, "price": 5.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 4, "location": 4,
"date": "2016-10-06T19:00:00Z", "date": "2016-10-06T19:00:00Z",
@ -142,7 +138,6 @@
"description": "Buffet \u00e0 la fin", "description": "Buffet \u00e0 la fin",
"title": "Concert Trouv\u00e8re", "title": "Concert Trouv\u00e8re",
"price": 20.0, "price": 20.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 5, "location": 5,
"date": "2016-11-30T12:00:00Z", "date": "2016-11-30T12:00:00Z",
@ -159,7 +154,6 @@
"description": "Vive les maths", "description": "Vive les maths",
"title": "Dessin \u00e0 la craie sur tableau noir", "title": "Dessin \u00e0 la craie sur tableau noir",
"price": 10.0, "price": 10.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 6, "location": 6,
"date": "2016-12-15T07:00:00Z", "date": "2016-12-15T07:00:00Z",
@ -176,7 +170,6 @@
"description": "Une pi\u00e8ce \u00e0 un personnage", "description": "Une pi\u00e8ce \u00e0 un personnage",
"title": "D\u00e9cors, d\u00e9montage en musique", "title": "D\u00e9cors, d\u00e9montage en musique",
"price": 0.0, "price": 0.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 3, "location": 3,
"date": "2016-12-26T07:00:00Z", "date": "2016-12-26T07:00:00Z",
@ -193,7 +186,6 @@
"description": "Annulera, annulera pas\u00a0?", "description": "Annulera, annulera pas\u00a0?",
"title": "La Nuit", "title": "La Nuit",
"price": 27.0, "price": 27.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 1, "location": 1,
"date": "2016-11-14T23:00:00Z", "date": "2016-11-14T23:00:00Z",
@ -210,7 +202,6 @@
"description": "Le boum fait sa carte blanche", "description": "Le boum fait sa carte blanche",
"title": "Turbomix", "title": "Turbomix",
"price": 10.0, "price": 10.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 2, "location": 2,
"date": "2017-01-10T20:00:00Z", "date": "2017-01-10T20:00:00Z",
@ -227,7 +218,6 @@
"description": "Unique repr\u00e9sentation", "description": "Unique repr\u00e9sentation",
"title": "Carinettes et trombone", "title": "Carinettes et trombone",
"price": 15.0, "price": 15.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 5, "location": 5,
"date": "2017-01-02T14:00:00Z", "date": "2017-01-02T14:00:00Z",
@ -244,7 +234,6 @@
"description": "Suivi d'une jam session", "description": "Suivi d'une jam session",
"title": "Percussion sur rondins", "title": "Percussion sur rondins",
"price": 5.0, "price": 5.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 4, "location": 4,
"date": "2017-01-13T14:00:00Z", "date": "2017-01-13T14:00:00Z",
@ -261,7 +250,6 @@
"description": "\u00c9preuve sportive et artistique", "description": "\u00c9preuve sportive et artistique",
"title": "Bassin aux ernests, nage libre", "title": "Bassin aux ernests, nage libre",
"price": 5.0, "price": 5.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 1, "location": 1,
"date": "2016-11-17T09:00:00Z", "date": "2016-11-17T09:00:00Z",
@ -278,7 +266,6 @@
"description": "Sonore", "description": "Sonore",
"title": "Chant du barde", "title": "Chant du barde",
"price": 13.0, "price": 13.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 2, "location": 2,
"date": "2017-02-26T07:00:00Z", "date": "2017-02-26T07:00:00Z",
@ -295,7 +282,6 @@
"description": "Cocorico", "description": "Cocorico",
"title": "Chant du coq", "title": "Chant du coq",
"price": 4.0, "price": 4.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 1, "location": 1,
"date": "2016-12-17T04:00:00Z", "date": "2016-12-17T04:00:00Z",

View file

@ -0,0 +1,89 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('bda', '0006_add_tirage_switch'),
]
operations = [
migrations.CreateModel(
name='CategorieSpectacle',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False,
auto_created=True, primary_key=True)),
('name', models.CharField(max_length=100, verbose_name='Nom',
unique=True)),
],
options={
'verbose_name': 'Cat\xe9gorie',
},
),
migrations.CreateModel(
name='Quote',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False,
auto_created=True, primary_key=True)),
('text', models.TextField(verbose_name='Citation')),
('author', models.CharField(max_length=200,
verbose_name='Auteur')),
],
),
migrations.AlterModelOptions(
name='spectacle',
options={'ordering': ('date', 'title'),
'verbose_name': 'Spectacle'},
),
migrations.RemoveField(
model_name='spectacle',
name='priority',
),
migrations.AddField(
model_name='spectacle',
name='ext_link',
field=models.CharField(
max_length=500,
verbose_name='Lien vers le site du spectacle',
blank=True),
),
migrations.AddField(
model_name='spectacle',
name='image',
field=models.ImageField(upload_to='imgs/shows/', null=True,
verbose_name='Image', blank=True),
),
migrations.AlterField(
model_name='tirage',
name='enable_do_tirage',
field=models.BooleanField(
default=False,
verbose_name='Le tirage peut \xeatre lanc\xe9'),
),
migrations.AlterField(
model_name='tirage',
name='tokens',
field=models.TextField(verbose_name='Graine(s) du tirage',
blank=True),
),
migrations.AddField(
model_name='spectacle',
name='category',
field=models.ForeignKey(blank=True, to='bda.CategorieSpectacle',
null=True),
),
migrations.AddField(
model_name='spectacle',
name='vips',
field=models.TextField(verbose_name='Personnalit\xe9s',
blank=True),
),
migrations.AddField(
model_name='quote',
name='spectacle',
field=models.ForeignKey(to='bda.Spectacle'),
),
]

View file

@ -29,7 +29,7 @@ class Tirage(models.Model):
tokens = models.TextField("Graine(s) du tirage", blank=True) tokens = models.TextField("Graine(s) du tirage", blank=True)
active = models.BooleanField("Tirage actif", default=False) active = models.BooleanField("Tirage actif", default=False)
enable_do_tirage = models.BooleanField("Le tirage peut être lancé", enable_do_tirage = models.BooleanField("Le tirage peut être lancé",
default=False) default=False)
def date_no_seconds(self): def date_no_seconds(self):
return self.fermeture.strftime('%d %b %Y %H:%M') return self.fermeture.strftime('%d %b %Y %H:%M')
@ -47,16 +47,32 @@ class Salle(models.Model):
return self.name return self.name
@python_2_unicode_compatible
class CategorieSpectacle(models.Model):
name = models.CharField('Nom', max_length=100, unique=True)
def __str__(self):
return self.name
class Meta:
verbose_name = "Catégorie"
@python_2_unicode_compatible @python_2_unicode_compatible
class Spectacle(models.Model): class Spectacle(models.Model):
title = models.CharField("Titre", max_length=300) title = models.CharField("Titre", max_length=300)
category = models.ForeignKey(CategorieSpectacle, blank=True, null=True)
date = models.DateTimeField("Date & heure") date = models.DateTimeField("Date & heure")
location = models.ForeignKey(Salle) location = models.ForeignKey(Salle)
vips = models.TextField('Personnalités', blank=True)
description = models.TextField("Description", blank=True) description = models.TextField("Description", blank=True)
slots_description = models.TextField("Description des places", blank=True) slots_description = models.TextField("Description des places", blank=True)
image = models.ImageField('Image', blank=True, null=True,
upload_to='imgs/shows/')
ext_link = models.CharField('Lien vers le site du spectacle', blank=True,
max_length=500)
price = models.FloatField("Prix d'une place") price = models.FloatField("Prix d'une place")
slots = models.IntegerField("Places") slots = models.IntegerField("Places")
priority = models.IntegerField("Priorité", default=1000)
tirage = models.ForeignKey(Tirage) tirage = models.ForeignKey(Tirage)
listing = models.BooleanField("Les places sont sur listing") listing = models.BooleanField("Les places sont sur listing")
rappel_sent = models.DateTimeField("Mail de rappel envoyé", blank=True, rappel_sent = models.DateTimeField("Mail de rappel envoyé", blank=True,
@ -64,7 +80,7 @@ class Spectacle(models.Model):
class Meta: class Meta:
verbose_name = "Spectacle" verbose_name = "Spectacle"
ordering = ("priority", "date", "title",) ordering = ("date", "title",)
def __repr__(self): def __repr__(self):
return "[%s]" % self return "[%s]" % self
@ -111,6 +127,13 @@ class Spectacle(models.Model):
# On renvoie la liste des destinataires # On renvoie la liste des destinataires
return members.values() return members.values()
class Quote(models.Model):
spectacle = models.ForeignKey(Spectacle)
text = models.TextField('Citation')
author = models.CharField('Auteur', max_length=200)
PAYMENT_TYPES = ( PAYMENT_TYPES = (
("cash", "Cash"), ("cash", "Cash"),
("cb", "CB"), ("cb", "CB"),

Binary file not shown.

View file

@ -0,0 +1,71 @@
{% load staticfiles %}
<!doctype html>
<html>
<head>
<style>
@font-face {
font-family: josefinsans;
src: url({% static "fonts/josefinsans.ttf" %});
}
*::-moz-selection {
background: #B0B0B0;
}
*::selection {
background: #B0B0B0;
}
.descTable{
width: auto;
margin: 0 auto 1em;
border-collapse: collapse;
border-spacing: 0;
font-size: 14px;
line-height: 2;
max-width: 100%;
background-color: transparent;
font-family: 'josefinsans', 'Arial';
font-weight: 700;
color: #5a5a5a;
}
</style>
<meta charset="utf8" />
</head>
<body>
{% for show in shows %}
<table class="descTable">
<thead>
<tr>
<th colspan="2"><p style="text-align:center;font-size:22px;">{{ show.title }}</p></th>
</tr>
</thead>
<tbody>
<tr>
<td><p style="text-align: left;">{{ show.location }}</p></td><td class="column-2"><p style="text-align: right;">{{ show.category }}</p></td>
</tr>
<tr>
<td><p style="text-align: left;">{{ show.date }}</p></td><td class="column-2"><p style="text-align: right;">{{ show.slots }} place{{ show.slots|pluralize}} {% if show.slots_description != "" %}({{ show.slots_description }}){% endif %}- {{ show.price }}</p></td>
</tr>
<tr>
<td colspan="2"><p style="text-align: justify;">{{ show.category }}</p></td>
</tr>
<tr>
<td colspan="2">
<p style="text-align: justify;">{{ show.description }}</p>
{% for quote in show.quote_set.all %}
<p style="text-align:center; font-style: italic;">«{{ quote.text }}»{% if show.quote.author %} - {{ quote.author }}{% endif %}</p>
{% endfor %}
</td>
</tr>
{% if show.image %}
<tr>
<td colspan="2"><p style="text-align:center;"><a href="{{ show.ext_link }}"><img style="display: inline;" src="{{ MEDIA_URL }}{{ show.image }}" alt="{{ show.title }}" width="200px"></a></p></td>
</tr>
{% endif %}
</tbody>
</table>
{% endfor %}
</body>
</html>

View file

@ -26,7 +26,7 @@
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
<script type="text/javascript" <script type="text/javascript"
src="{% static "js/jquery.min.js" %}"></script> src="{% static "js/jquery.min.js" %}"></script>
@ -39,14 +39,15 @@
</script> </script>
<script> <script>
jQuery(document).ready(function($) { jQuery(document).ready(function($) {
$(".clickable-row").click(function() { $(".clickable-row").click(function() {
window.document.location = $(this).data("href"); window.document.location = $(this).data("href");
}); });
}); });
</script> </script>
<h3> Exports </h3> <h3> Exports </h3>
<ul> <ul>
<li><a href="{% url 'bda-unpaid' tirage_id %}">Mailing list impayés</a> <li><a href="{% url 'bda-unpaid' tirage_id %}">Mailing list impayés</a>
<li><a href="{% url 'bda-descriptions' tirage_id %}">Lien vers les descriptions des spectacles, à utiliser dans une page wordpress</a>
</ul> </ul>
{% endblock %} {% endblock %}

View file

@ -5,6 +5,7 @@ from __future__ import print_function
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf.urls import url from django.conf.urls import url
from gestioncof.decorators import buro_required
from bda.views import SpectacleListView from bda.views import SpectacleListView
from bda import views from bda import views
@ -23,7 +24,7 @@ urlpatterns = [
name='bda-etat-places'), name='bda-etat-places'),
url(r'^tirage/(?P<tirage_id>\d+)$', views.tirage), url(r'^tirage/(?P<tirage_id>\d+)$', views.tirage),
url(r'^spectacles/(?P<tirage_id>\d+)$', url(r'^spectacles/(?P<tirage_id>\d+)$',
SpectacleListView.as_view(), buro_required(SpectacleListView.as_view()),
name="bda-liste-spectacles"), name="bda-liste-spectacles"),
url(r'^spectacles/(?P<tirage_id>\d+)/(?P<spectacle_id>\d+)$', url(r'^spectacles/(?P<tirage_id>\d+)/(?P<spectacle_id>\d+)$',
views.spectacle, views.spectacle,
@ -32,4 +33,6 @@ urlpatterns = [
views.unpaid, views.unpaid,
name="bda-unpaid"), name="bda-unpaid"),
url(r'^mails-rappel/(?P<spectacle_id>\d+)$', views.send_rappel), url(r'^mails-rappel/(?P<spectacle_id>\d+)$', views.send_rappel),
url(r'^descriptions/(?P<tirage_id>\d+)$', views.descriptions_spectacles,
name='bda-descriptions'),
] ]

View file

@ -10,13 +10,13 @@ from django.db import models
from django.db.models import Count from django.db.models import Count
from django.core import serializers from django.core import serializers
from django.forms.models import inlineformset_factory from django.forms.models import inlineformset_factory
from django.http import HttpResponseBadRequest
import hashlib import hashlib
from django.core.mail import send_mail from django.core.mail import send_mail
from django.utils import timezone from django.utils import timezone
from django.views.generic.list import ListView from django.views.generic.list import ListView
from datetime import timedelta
import time import time
from gestioncof.decorators import cof_required, buro_required from gestioncof.decorators import cof_required, buro_required
@ -110,8 +110,7 @@ def places(request, tirage_id):
def inscription(request, tirage_id): def inscription(request, tirage_id):
tirage = get_object_or_404(Tirage, id=tirage_id) tirage = get_object_or_404(Tirage, id=tirage_id)
if timezone.now() < tirage.ouverture: if timezone.now() < tirage.ouverture:
error_desc = "Ouverture le %s" % ( error_desc = tirage.ouverture.strftime('Ouverture le %d %b %Y à %H:%M')
tirage.ouverture.strftime('%d %b %Y à %H:%M'))
return render(request, 'resume_inscription.html', return render(request, 'resume_inscription.html',
{"error_title": "Le tirage n'est pas encore ouvert !", {"error_title": "Le tirage n'est pas encore ouvert !",
"error_description": error_desc}) "error_description": error_desc})
@ -366,3 +365,19 @@ def send_rappel(request, spectacle_id):
else: else:
ctxt['sent'] = False ctxt['sent'] = False
return render(request, "mails-rappel.html", ctxt) return render(request, "mails-rappel.html", ctxt)
def descriptions_spectacles(request, tirage_id):
tirage = get_object_or_404(Tirage, id=tirage_id)
shows_qs = tirage.spectacle_set
category_name = request.GET.get('category', '')
location_id = request.GET.get('location', '')
if category_name:
shows_qs = shows_qs.filter(category__name=category_name)
if location_id:
try:
shows_qs = shows_qs.filter(location__id=int(location_id))
except ValueError:
return HttpResponseBadRequest(
"La variable GET 'location' doit contenir un entier")
return render(request, 'descriptions.html', {'shows': shows_qs.all()})

View file

@ -16,7 +16,8 @@ from django.contrib.auth import views as django_views
from django_cas_ng import views as django_cas_views from django_cas_ng import views as django_cas_views
from gestioncof import views as gestioncof_views, csv_views from gestioncof import views as gestioncof_views, csv_views
from gestioncof.urls import export_patterns, petitcours_patterns, \ from gestioncof.urls import export_patterns, petitcours_patterns, \
surveys_patterns, events_patterns, calendar_patterns surveys_patterns, events_patterns, calendar_patterns, \
clubs_patterns
from gestioncof.autocomplete import autocomplete from gestioncof.autocomplete import autocomplete
@ -38,6 +39,8 @@ urlpatterns = [
url(r'^event/', include(events_patterns)), url(r'^event/', include(events_patterns)),
# Calendrier # Calendrier
url(r'^calendar/', include(calendar_patterns)), url(r'^calendar/', include(calendar_patterns)),
# Clubs
url(r'^clubs/', include(clubs_patterns)),
# Authentification # Authentification
url(r'^cof/denied$', TemplateView.as_view(template_name='cof-denied.html'), url(r'^cof/denied$', TemplateView.as_view(template_name='cof-denied.html'),
name="cof-denied"), name="cof-denied"),

View file

@ -4,6 +4,7 @@ from __future__ import division
from __future__ import print_function from __future__ import print_function
from __future__ import unicode_literals from __future__ import unicode_literals
from django import forms
from django.contrib import admin from django.contrib import admin
from gestioncof.models import SurveyQuestionAnswer, SurveyQuestion, \ from gestioncof.models import SurveyQuestionAnswer, SurveyQuestion, \
CofProfile, EventOption, EventOptionChoice, Event, Club, CustomMail, \ CofProfile, EventOption, EventOptionChoice, Event, Club, CustomMail, \
@ -232,6 +233,25 @@ class PetitCoursDemandeAdmin(admin.ModelAdmin):
class CustomMailAdmin(admin.ModelAdmin): class CustomMailAdmin(admin.ModelAdmin):
search_fields = ('shortname', 'title') search_fields = ('shortname', 'title')
class ClubAdminForm(forms.ModelForm):
def clean(self):
cleaned_data = super(ClubAdminForm, self).clean()
respos = cleaned_data.get('respos')
members = cleaned_data.get('membres')
for respo in respos.all():
if respo not in members:
raise forms.ValidationError(
"Erreur : le respo %s n'est pas membre du club."
% respo.get_full_name())
return cleaned_data
class ClubAdmin(admin.ModelAdmin):
list_display = ['name']
form = ClubAdminForm
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)
@ -239,7 +259,7 @@ admin.site.register(EventOption, EventOptionAdmin)
admin.site.unregister(User) admin.site.unregister(User)
admin.site.register(User, UserProfileAdmin) admin.site.register(User, UserProfileAdmin)
admin.site.register(CofProfile) admin.site.register(CofProfile)
admin.site.register(Club) admin.site.register(Club, ClubAdmin)
admin.site.register(CustomMail) admin.site.register(CustomMail)
admin.site.register(PetitCoursSubject) admin.site.register(PetitCoursSubject)
admin.site.register(PetitCoursAbility, PetitCoursAbilityAdmin) admin.site.register(PetitCoursAbility, PetitCoursAbilityAdmin)

View file

@ -0,0 +1,10 @@
[
{
"fields": {
"domain": "localhost",
"name": "GestioCOF - dev - local"
},
"model": "sites.site",
"pk": 1
}
]

View file

@ -8,10 +8,12 @@ from django import forms
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.forms.widgets import RadioSelect, CheckboxSelectMultiple from django.forms.widgets import RadioSelect, CheckboxSelectMultiple
from django.forms.formsets import BaseFormSet, formset_factory
from django.db.models import Max from django.db.models import Max
from django.core.validators import MinLengthValidator
from gestioncof.models import CofProfile, EventCommentValue, \ from gestioncof.models import CofProfile, EventCommentValue, \
CalendarSubscription CalendarSubscription, Club
from gestioncof.widgets import TriStateCheckbox from gestioncof.widgets import TriStateCheckbox
from gestioncof.shared import lock_table, unlock_table from gestioncof.shared import lock_table, unlock_table
@ -183,14 +185,6 @@ class UserProfileForm(forms.ModelForm):
super(UserProfileForm, self).__init__(*args, **kw) super(UserProfileForm, self).__init__(*args, **kw)
self.fields['first_name'].initial = self.instance.user.first_name self.fields['first_name'].initial = self.instance.user.first_name
self.fields['last_name'].initial = self.instance.user.last_name self.fields['last_name'].initial = self.instance.user.last_name
self.fields.keyOrder = [
'first_name',
'last_name',
'phone',
'mailing_cof',
'mailing_bda',
'mailing_bda_revente',
]
def save(self, *args, **kw): def save(self, *args, **kw):
super(UserProfileForm, self).save(*args, **kw) super(UserProfileForm, self).save(*args, **kw)
@ -200,8 +194,8 @@ class UserProfileForm(forms.ModelForm):
class Meta: class Meta:
model = CofProfile model = CofProfile
fields = ("phone", "mailing_cof", "mailing_bda", fields = ["first_name", "last_name", "phone", "mailing_cof",
"mailing_bda_revente", ) "mailing_bda", "mailing_bda_revente"]
class RegistrationUserForm(forms.ModelForm): class RegistrationUserForm(forms.ModelForm):
@ -209,11 +203,40 @@ class RegistrationUserForm(forms.ModelForm):
super(RegistrationUserForm, self).__init__(*args, **kw) super(RegistrationUserForm, self).__init__(*args, **kw)
self.fields['username'].help_text = "" self.fields['username'].help_text = ""
def force_long_username(self):
self.fields['username'].validators = [MinLengthValidator(9)]
class Meta: class Meta:
model = User model = User
fields = ("username", "first_name", "last_name", "email") fields = ("username", "first_name", "last_name", "email")
class RegistrationPassUserForm(RegistrationUserForm):
"""
Formulaire pour changer le mot de passe d'un utilisateur.
"""
password1 = forms.CharField(label=_('Mot de passe'),
widget=forms.PasswordInput)
password2 = forms.CharField(label=_('Confirmation du mot de passe'),
widget=forms.PasswordInput)
def clean_password2(self):
pass1 = self.cleaned_data['password1']
pass2 = self.cleaned_data['password2']
if pass1 and pass2:
if pass1 != pass2:
raise forms.ValidationError(_('Mots de passe non identiques.'))
return pass2
def save(self, commit=True, *args, **kwargs):
user = super(RegistrationPassUserForm, self).save(commit, *args,
**kwargs)
user.set_password(self.cleaned_data['password2'])
if commit:
user.save()
return user
class RegistrationProfileForm(forms.ModelForm): class RegistrationProfileForm(forms.ModelForm):
def __init__(self, *args, **kw): def __init__(self, *args, **kw):
super(RegistrationProfileForm, self).__init__(*args, **kw) super(RegistrationProfileForm, self).__init__(*args, **kw)
@ -263,17 +286,15 @@ STATUS_CHOICES = (('no', 'Non'),
class AdminEventForm(forms.Form): class AdminEventForm(forms.Form):
status = forms.ChoiceField(label="Inscription", status = forms.ChoiceField(label="Inscription", initial="no",
choices=STATUS_CHOICES, widget=RadioSelect) choices=STATUS_CHOICES, widget=RadioSelect)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
event = kwargs.pop("event") self.event = kwargs.pop("event")
self.event = event
registration = kwargs.pop("current_registration", None) registration = kwargs.pop("current_registration", None)
current_choices = \ current_choices, paid = \
registration.options.all() if registration is not None\ (registration.options.all(), registration.paid) \
else [] if registration is not None else ([], None)
paid = kwargs.pop("paid", None)
if paid is True: if paid is True:
kwargs["initial"] = {"status": "paid"} kwargs["initial"] = {"status": "paid"}
elif paid is False: elif paid is False:
@ -288,7 +309,7 @@ class AdminEventForm(forms.Form):
else: else:
choices[choice.event_option.id].append(choice.id) choices[choice.event_option.id].append(choice.id)
all_choices = choices all_choices = choices
for option in event.options.all(): for option in self.event.options.all():
choices = [(choice.id, choice.value) choices = [(choice.id, choice.value)
for choice in option.choices.all()] for choice in option.choices.all()]
if option.multi_choices: if option.multi_choices:
@ -310,7 +331,7 @@ class AdminEventForm(forms.Form):
initial=initial) initial=initial)
field.option_id = option.id field.option_id = option.id
self.fields["option_%d" % option.id] = field self.fields["option_%d" % option.id] = field
for commentfield in event.commentfields.all(): for commentfield in self.event.commentfields.all():
initial = commentfield.default initial = commentfield.default
if registration is not None: if registration is not None:
try: try:
@ -338,6 +359,22 @@ class AdminEventForm(forms.Form):
yield (self.fields[name].comment_id, value) yield (self.fields[name].comment_id, value)
class BaseEventRegistrationFormset(BaseFormSet):
def __init__(self, *args, **kwargs):
self.events = kwargs.pop('events')
self.current_registrations = kwargs.pop('current_registrations', None)
self.extra = len(self.events)
super(BaseEventRegistrationFormset, self).__init__(*args, **kwargs)
def _construct_form(self, index, **kwargs):
kwargs['event'] = self.events[index]
if self.current_registrations is not None:
kwargs['current_registration'] = self.current_registrations[index]
return super(BaseEventRegistrationFormset, self)._construct_form(
index, **kwargs)
EventFormset = formset_factory(AdminEventForm, BaseEventRegistrationFormset)
class CalendarForm(forms.ModelForm): class CalendarForm(forms.ModelForm):
subscribe_to_events = forms.BooleanField( subscribe_to_events = forms.BooleanField(
initial=True, initial=True,
@ -348,9 +385,21 @@ class CalendarForm(forms.ModelForm):
other_shows = forms.ModelMultipleChoiceField( other_shows = forms.ModelMultipleChoiceField(
label="Spectacles supplémentaires.", label="Spectacles supplémentaires.",
queryset=Spectacle.objects.filter(tirage__active=True), queryset=Spectacle.objects.filter(tirage__active=True),
widget=forms.CheckboxSelectMultiple) widget=forms.CheckboxSelectMultiple,
required=False)
class Meta: class Meta:
model = CalendarSubscription model = CalendarSubscription
fields = ['subscribe_to_events', 'subscribe_to_my_shows', fields = ['subscribe_to_events', 'subscribe_to_my_shows',
'other_shows'] 'other_shows']
class ClubsForm(forms.Form):
"""
Formulaire d'inscription d'un membre à plusieurs clubs du COF.
"""
clubs = forms.ModelMultipleChoiceField(
label="Inscriptions aux clubs du COF",
queryset=Club.objects.all(),
widget=forms.CheckboxSelectMultiple,
required=False)

View file

@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('gestioncof', '0006_add_calendar'),
]
operations = [
migrations.AlterField(
model_name='club',
name='name',
field=models.CharField(unique=True, max_length=200,
verbose_name='Nom')
),
migrations.AlterField(
model_name='club',
name='description',
field=models.TextField(verbose_name='Description', blank=True)
),
migrations.AlterField(
model_name='club',
name='membres',
field=models.ManyToManyField(related_name='clubs',
to=settings.AUTH_USER_MODEL,
blank=True),
),
migrations.AlterField(
model_name='club',
name='respos',
field=models.ManyToManyField(related_name='clubs_geres',
to=settings.AUTH_USER_MODEL,
blank=True),
),
migrations.AlterField(
model_name='event',
name='start_date',
field=models.DateTimeField(null=True,
verbose_name='Date de d\xe9but',
blank=True),
),
]

View file

@ -86,10 +86,11 @@ post_save.connect(create_user_profile, sender=User)
@python_2_unicode_compatible @python_2_unicode_compatible
class Club(models.Model): class Club(models.Model):
name = models.CharField("Nom", max_length=200) name = models.CharField("Nom", max_length=200, unique=True)
description = models.TextField("Description") description = models.TextField("Description", blank=True)
respos = models.ManyToManyField(User, related_name="clubs_geres") respos = models.ManyToManyField(User, related_name="clubs_geres",
membres = models.ManyToManyField(User, related_name="clubs") blank=True)
membres = models.ManyToManyField(User, related_name="clubs", blank=True)
def __str__(self): def __str__(self):
return self.name return self.name

View file

@ -825,6 +825,16 @@ input#search_autocomplete:focus {
color: #343a4a; color: #343a4a;
} }
input[type=number][readonly] {
-moz-appearance:textfield;
}
input[type=number][readonly]::-webkit-inner-spin-button,
input[type=number][readonly]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
.autocomplete { .autocomplete {
margin-bottom:5px; margin-bottom:5px;
} }

View file

@ -37,7 +37,7 @@ souscrire aux événements du COF et/ou aux spectacles BdA.
<form action="" method="post"> <form action="" method="post">
{% csrf_token %} {% csrf_token %}
{{ form.as_p }} {{ form.as_p }}
<input type="submit" value="Enregistrer" /> <input type="submit" value="Enregistrer" class="btn btn-primary" />
</form> </form>
{% endblock %} {% endblock %}

View file

@ -37,26 +37,29 @@
{% for tirage in open_tirages %} {% for tirage in open_tirages %}
<ul> <ul>
<h4>{{ tirage.title }}</h4> <h4>{{ tirage.title }}</h4>
<li><a href="{% url "bda-tirage-inscription" tirage.id %}">Inscription</a></li> {% if tirage.fermeture > now %}
<li><a href="{% url "bda-etat-places" tirage.id %}">État des demandes</a> <li><a href="{% url "bda-tirage-inscription" tirage.id %}">Inscription</a></li>
<li><a href="{% url "bda-places-attribuees" tirage.id %}">Mes places</a></li> <li><a href="{% url "bda-etat-places" tirage.id %}">État des demandes</a></li>
<li><a href="{% url "bda-revente" tirage.id %}">Revendre une place</a></li> {% else %}
<li><a href="{% url "bda-places-attribuees" tirage.id %}">Mes places</a></li>
<li><a href="{% url "bda-revente" tirage.id %}">Revendre une place</a></li>
{% endif %}
</ul> </ul>
{% endfor %} {% endfor %}
</div> </div>
{% endif %} {% endif %}
{% endif %}
<h3 class="block-title">Divers<span class="pull-right glyphicon glyphicon-question-sign"></span></h3> <h3 class="block-title">Divers<span class="pull-right glyphicon glyphicon-question-sign"></span></h3>
<div class="hm-block"> <div class="hm-block">
<ul> <ul>
<li><a href="{% url "gestioncof.views.calendar" %}">Calendrier dynamique</a></li> <li><a href="{% url "gestioncof.views.calendar" %}">Calendrier dynamique</a></li>
{% if user.profile.is_cof %}<li><a href="{% url "petits-cours-inscription" %}">Inscription pour donner des petits cours</a></li>{% endif %} {% if user.profile.is_cof %}<li><a href="{% url "petits-cours-inscription" %}">Inscription pour donner des petits cours</a></li>{% endif %}
<li><a href="{% url "gestioncof.views.profile" %}">Éditer mon profil</a></li> <li><a href="{% url "gestioncof.views.profile" %}">Éditer mon profil</a></li>
{% if not user.profile.login_clipper %}<li><a href="{% url "django.contrib.auth.views.password_change" %}">Changer mon mot de passe</a></li>{% endif %} {% if not user.profile.login_clipper %}<li><a href="{% url "django.contrib.auth.views.password_change" %}">Changer mon mot de passe</a></li>{% endif %}
</ul> </ul>
</div> </div>
{% endif %}
</div> </div>
{% if user.profile.is_buro %} {% if user.profile.is_buro %}
<div class="col-sm-6 buro-user-hm"> <div class="col-sm-6 buro-user-hm">
@ -67,6 +70,7 @@
<li><a href="{% url "admin:index" %}">Administration générale</a></li> <li><a href="{% url "admin:index" %}">Administration générale</a></li>
<li><a href="{% url "petits-cours-demandes-list" %}">Demandes de petits cours</a></li> <li><a href="{% url "petits-cours-demandes-list" %}">Demandes de petits cours</a></li>
<li><a href="{% url "gestioncof.views.registration" %}">Inscription d'un nouveau membre</a></li> <li><a href="{% url "gestioncof.views.registration" %}">Inscription d'un nouveau membre</a></li>
<li><a href="{% url "liste-clubs" %}">Gestion des clubs</a></li>
</ul> </ul>
<ul> <ul>
<h4>Évènements & Sondages</h4> <h4>Évènements & Sondages</h4>
@ -80,12 +84,15 @@
</div> </div>
<h3 class="block-title">Gestion tirages BDA<span class="pull-right glyphicon glyphicon-list"></span></h3> <h3 class="block-title">Gestion tirages BDA<span class="pull-right glyphicon glyphicon-list"></span></h3>
<div class="hm-block"> <div class="hm-block">
{% if open_tirages %} {% if active_tirages %}
{% for tirage in open_tirages %} {% for tirage in active_tirages %}
<ul> <ul>
<h4>{{ tirage.title }}</h4> <h4>{{ tirage.title }}</h4>
<li><a href="{% url "bda-liste-spectacles" tirage.id %}">Spectacles</a></li> <li><a href="{% url "bda-liste-spectacles" tirage.id %}">Spectacles</a></li>
<li><a href="{% url "admin:bda_participant_changelist" %}?tirage__id__exact={{ tirage.id }}">Participants</a></li> <li><a href="{% url "admin:bda_participant_changelist" %}?tirage__id__exact={{ tirage.id }}">Participants</a></li>
{% if tirage.fermeture < now %}
<li><a href="{% url "bda-etat-places" tirage.id %}">Ratios</a></li>
{% endif %}
</ul> </ul>
{% endfor %} {% endfor %}
{% else %} {% else %}

View file

@ -0,0 +1,25 @@
{% extends "base_title.html" %}
{% block page_size %}col-sm-8{% endblock %}
{% block realcontent %}
<h2>Clubs enregistrés sur GestioCOF</h2>
<ul>
{% for club in owned_clubs %}
<li>
<a href="{% url "membres-club" club.name %}">
{{ club }} ({% for respo in club.respos.all %}{{ respo.get_full_name }}, {% empty %}pas de respo{% endfor %})
</a>
</li>
{% endfor %}
{% if other_clubs %}
{% for club in other_clubs %}
<li>
<p>
{{ club }} ({% for respo in club.respos.all %}{{ respo.get_full_name }}, {% empty %}pas de respo{% endfor %})
</p>
</li>
{% endfor %}
{% endif %}
</ul>
{% endblock %}

View file

@ -0,0 +1,41 @@
{% extends "base_title.html" %}
{% block realcontent %}
<h2>{{ club }}</h2>
{% if club.respos.exists %}
<h3>Respo{{ club.respos.all|pluralize }}</h3>
<table class="table table-striped">
{% for member in club.respos.all %}
<tr>
<td>{{ member }}</td>
<td>{{ member.email }}</td>
<td><a class="glyphicon glyphicon-arrow-down"
href="{% url "change-respo" club.name member.id %}"></a></td>
</tr>
{% endfor %}
</table>
{% else %}
<h3>Pas de respo</h3>
{% endif %}
{% if club.membres.exists %}
<h3>Liste des membres</h3>
<table class="table table-striped">
{% for member in members_no_respo %}
<tr>
<td>{{ member }}</td>
<td>{{ member.email }}</td>
<td><a class="glyphicon glyphicon-arrow-up"
href="{% url "change-respo" club.name member.id %}"></a></td>
</tr>
{% endfor %}
</table>
{% else %}
Ce club ne comporte actuellement aucun membre.
{% endif %}
{% endblock %}

View file

@ -12,16 +12,19 @@
<table> <table>
{{ user_form | bootstrap }} {{ user_form | bootstrap }}
{{ profile_form | bootstrap }} {{ profile_form | bootstrap }}
{% if event_forms %}
</table> </table>
{% for event_form in event_forms %} <hr />
<table>
{{ clubs_form | bootstrap }}
</table>
{{ event_formset.management_form }}
{% for event_form in event_formset %}
<hr /> <hr />
<h3>Inscription {{ event_form.event.title }} :</h2> <h3>Inscription {{ event_form.event.title }}&nbsp;:</h3>
<table> <table>
{{ event_form | bootstrap }} {{ event_form | bootstrap }}
</table> </table>
{% endfor %} {% endfor %}
{% endif %}
{% if login_clipper or member %} {% if login_clipper or member %}
<input type="hidden" name="user_exists" value="1" /> <input type="hidden" name="user_exists" value="1" />
{% endif %} {% endif %}

View file

@ -52,3 +52,10 @@ calendar_patterns = [
url(r'^(?P<token>[a-z0-9-]+)/calendar.ics$', url(r'^(?P<token>[a-z0-9-]+)/calendar.ics$',
'gestioncof.views.calendar_ics') 'gestioncof.views.calendar_ics')
] ]
clubs_patterns = [
url(r'^membres/(?P<name>\w+)', views.membres_club, name='membres-club'),
url(r'^liste', views.liste_clubs, name='liste-clubs'),
url(r'^change_respo/(?P<club_name>\w+)/(?P<user_id>\d+)',
views.change_respo, name='change-respo'),
]

View file

@ -10,10 +10,11 @@ from datetime import timedelta
from icalendar import Calendar, Event as Vevent from icalendar import Calendar, Event as Vevent
from django.shortcuts import redirect, get_object_or_404, render from django.shortcuts import redirect, get_object_or_404, render
from django.http import Http404, HttpResponse from django.http import Http404, HttpResponse, HttpResponseForbidden
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.views import login as django_login_view from django.contrib.auth.views import login as django_login_view
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.utils import timezone
import django.utils.six as six import django.utils.six as six
from gestioncof.models import Survey, SurveyAnswer, SurveyQuestion, \ from gestioncof.models import Survey, SurveyAnswer, SurveyQuestion, \
@ -23,11 +24,12 @@ from gestioncof.models import Event, EventRegistration, EventOption, \
from gestioncof.models import EventCommentField, EventCommentValue, \ from gestioncof.models import EventCommentField, EventCommentValue, \
CalendarSubscription CalendarSubscription
from gestioncof.shared import send_custom_mail from gestioncof.shared import send_custom_mail
from gestioncof.models import CofProfile, Clipper from gestioncof.models import CofProfile, Clipper, Club
from gestioncof.decorators import buro_required, cof_required from gestioncof.decorators import buro_required, cof_required
from gestioncof.forms import UserProfileForm, EventStatusFilterForm, \ from gestioncof.forms import UserProfileForm, EventStatusFilterForm, \
SurveyForm, SurveyStatusFilterForm, RegistrationUserForm, \ SurveyForm, SurveyStatusFilterForm, RegistrationUserForm, \
RegistrationProfileForm, AdminEventForm, EventForm, CalendarForm RegistrationProfileForm, EventForm, CalendarForm, EventFormset, \
RegistrationPassUserForm, ClubsForm
from bda.models import Tirage, Spectacle from bda.models import Tirage, Spectacle
@ -40,7 +42,11 @@ def home(request):
Survey.objects.filter(survey_open=True, old=False).all(), Survey.objects.filter(survey_open=True, old=False).all(),
"open_events": "open_events":
Event.objects.filter(registration_open=True, old=False).all(), Event.objects.filter(registration_open=True, old=False).all(),
"open_tirages": Tirage.objects.filter(active=True).all()} "active_tirages": Tirage.objects.filter(active=True).all(),
"open_tirages":
Tirage.objects.filter(active=True,
ouverture__lte=timezone.now()).all(),
"now": timezone.now()}
return render(request, "home.html", data) return render(request, "home.html", data)
@ -91,7 +97,7 @@ def logout(request):
@login_required @login_required
def survey(request, survey_id): def survey(request, survey_id):
survey = get_object_or_404(Survey, id=survey_id) survey = get_object_or_404(Survey, id=survey_id)
if not survey.survey_open: if not survey.survey_open or survey.old:
raise Http404 raise Http404
success = False success = False
deleted = False deleted = False
@ -188,7 +194,7 @@ def update_event_form_comments(event, form, registration):
@login_required @login_required
def event(request, event_id): def event(request, event_id):
event = get_object_or_404(Event, id=event_id) event = get_object_or_404(Event, id=event_id)
if not event.registration_open: if (not event.registration_open) or event.old:
raise Http404 raise Http404
success = False success = False
if request.method == "POST": if request.method == "POST":
@ -295,7 +301,7 @@ def survey_status(request, survey_id):
"form": form}) "form": form})
@login_required @cof_required
def profile(request): def profile(request):
success = False success = False
if request.method == "POST": if request.method == "POST":
@ -313,45 +319,6 @@ def registration_set_ro_fields(user_form, profile_form):
profile_form.fields['login_clipper'].widget.attrs['readonly'] = True profile_form.fields['login_clipper'].widget.attrs['readonly'] = True
@buro_required
def registration_form(request, login_clipper=None, username=None):
member = None
if login_clipper:
clipper = get_object_or_404(Clipper, username=login_clipper)
try: # check if the given user is already registered
member = User.objects.filter(username=login_clipper).get()
username = member.username
login_clipper = None
except User.DoesNotExist:
# new user, but prefill
user_form = RegistrationUserForm()
profile_form = RegistrationProfileForm()
user_form.fields['username'].initial = login_clipper
user_form.fields['email'].initial = \
login_clipper + "@clipper.ens.fr"
profile_form.fields['login_clipper'].initial = login_clipper
if clipper.fullname:
bits = clipper.fullname.split(" ")
user_form.fields['first_name'].initial = bits[0]
if len(bits) > 1:
user_form.fields['last_name'].initial = " ".join(bits[1:])
registration_set_ro_fields(user_form, profile_form)
if username:
member = get_object_or_404(User, username=username)
(profile, _) = CofProfile.objects.get_or_create(user=member)
# already existing, prefill
user_form = RegistrationUserForm(instance=member)
profile_form = RegistrationProfileForm(instance=profile)
registration_set_ro_fields(user_form, profile_form)
elif not login_clipper:
# new user
user_form = RegistrationUserForm()
profile_form = RegistrationProfileForm()
return render(request, "registration_form.html",
{"user_form": user_form, "profile_form": profile_form,
"member": member, "login_clipper": login_clipper})
@buro_required @buro_required
def registration_form2(request, login_clipper=None, username=None): def registration_form2(request, login_clipper=None, username=None):
events = Event.objects.filter(old=False).all() events = Event.objects.filter(old=False).all()
@ -359,24 +326,27 @@ def registration_form2(request, login_clipper=None, username=None):
if login_clipper: if login_clipper:
clipper = get_object_or_404(Clipper, username=login_clipper) clipper = get_object_or_404(Clipper, username=login_clipper)
try: # check if the given user is already registered try: # check if the given user is already registered
member = User.objects.filter(username=login_clipper).get() member = User.objects.get(username=login_clipper)
username = member.username username = member.username
login_clipper = None login_clipper = None
except User.DoesNotExist: except User.DoesNotExist:
# new user, but prefill # new user, but prefill
user_form = RegistrationUserForm() # user
profile_form = RegistrationProfileForm() user_form = RegistrationUserForm(initial={
event_forms = [AdminEventForm(event=event) for event in events] 'username': login_clipper,
user_form.fields['username'].initial = login_clipper 'email': "%s@clipper.ens.fr" % login_clipper})
user_form.fields['email'].initial = \
login_clipper + "@clipper.ens.fr"
profile_form.fields['login_clipper'].initial = login_clipper
if clipper.fullname: if clipper.fullname:
bits = clipper.fullname.split(" ") bits = clipper.fullname.split(" ")
user_form.fields['first_name'].initial = bits[0] user_form.fields['first_name'].initial = bits[0]
if len(bits) > 1: if len(bits) > 1:
user_form.fields['last_name'].initial = " ".join(bits[1:]) user_form.fields['last_name'].initial = " ".join(bits[1:])
# profile
profile_form = RegistrationProfileForm(initial={
'login_clipper': login_clipper})
registration_set_ro_fields(user_form, profile_form) registration_set_ro_fields(user_form, profile_form)
# events & clubs
event_formset = EventFormset(events=events, prefix='events')
clubs_form = ClubsForm(initial={'clubs': member.clubs.all()})
if username: if username:
member = get_object_or_404(User, username=username) member = get_object_or_404(User, username=username)
(profile, _) = CofProfile.objects.get_or_create(user=member) (profile, _) = CofProfile.objects.get_or_create(user=member)
@ -384,121 +354,185 @@ def registration_form2(request, login_clipper=None, username=None):
user_form = RegistrationUserForm(instance=member) user_form = RegistrationUserForm(instance=member)
profile_form = RegistrationProfileForm(instance=profile) profile_form = RegistrationProfileForm(instance=profile)
registration_set_ro_fields(user_form, profile_form) registration_set_ro_fields(user_form, profile_form)
event_forms = [] # events
current_registrations = []
for event in events: for event in events:
try: try:
current_registration = EventRegistration.objects.get( current_registrations.append(
user=member, event=event) EventRegistration.objects.get(user=member, event=event))
form = AdminEventForm(
event=event,
current_registration=current_registration,
paid=current_registration.paid)
except EventRegistration.DoesNotExist: except EventRegistration.DoesNotExist:
form = AdminEventForm(event=event) current_registrations.append(None)
event_forms.append(form) event_formset = EventFormset(
events=events, prefix='events',
current_registrations=current_registrations)
# Clubs
clubs_form = ClubsForm(initial={'clubs': member.clubs.all()})
elif not login_clipper: elif not login_clipper:
# new user # new user
user_form = RegistrationUserForm() user_form = RegistrationPassUserForm()
user_form.force_long_username()
profile_form = RegistrationProfileForm() profile_form = RegistrationProfileForm()
event_forms = [AdminEventForm(event=event) for event in events] event_formset = EventFormset(events=events, prefix='events')
clubs_form = ClubsForm()
return render(request, "registration_form.html", return render(request, "registration_form.html",
{"user_form": user_form, "profile_form": profile_form, {"member": member, "login_clipper": login_clipper,
"member": member, "login_clipper": login_clipper, "user_form": user_form,
"event_forms": event_forms}) "profile_form": profile_form,
"event_formset": event_formset,
"clubs_form": clubs_form})
@buro_required @buro_required
def registration(request): def registration(request):
if request.POST: if request.POST:
request_dict = request.POST.copy() request_dict = request.POST.copy()
# num ne peut pas être défini manuellement
if "num" in request_dict: if "num" in request_dict:
del request_dict["num"] del request_dict["num"]
success = False
user_form = RegistrationUserForm(request_dict)
profile_form = RegistrationProfileForm(request_dict)
events = Event.objects.filter(old=False).all()
event_forms = \
[AdminEventForm(request_dict, event=event) for event in events]
user_form.is_valid()
profile_form.is_valid()
for event_form in event_forms:
event_form.is_valid()
member = None member = None
login_clipper = None login_clipper = None
success = False
# -----
# Remplissage des formulaires
# -----
if 'password1' in request_dict or 'password2' in request_dict:
user_form = RegistrationPassUserForm(request_dict)
else:
user_form = RegistrationUserForm(request_dict)
profile_form = RegistrationProfileForm(request_dict)
clubs_form = ClubsForm(request_dict)
events = Event.objects.filter(old=False).all()
event_formset = EventFormset(events=events, data=request_dict,
prefix='events')
if "user_exists" in request_dict and request_dict["user_exists"]: if "user_exists" in request_dict and request_dict["user_exists"]:
username = request_dict["username"] username = request_dict["username"]
try: try:
member = User.objects.filter(username=username).get() member = User.objects.get(username=username)
(profile, _) = CofProfile.objects.get_or_create(user=member)
user_form = RegistrationUserForm(request_dict, instance=member) user_form = RegistrationUserForm(request_dict, instance=member)
profile_form = RegistrationProfileForm(request_dict,
instance=profile)
except User.DoesNotExist: except User.DoesNotExist:
try: try:
clipper = Clipper.objects.filter(username=username).get() clipper = Clipper.objects.get(username=username)
login_clipper = clipper.username login_clipper = clipper.username
except Clipper.DoesNotExist: except Clipper.DoesNotExist:
pass user_form.force_long_username()
for form in event_forms: else:
if not form.is_valid(): user_form.force_long_username()
break
if form.cleaned_data['status'] == 'no': # -----
continue # Validation des formulaires
all_choices = get_event_form_choices(form.event, form) # -----
if user_form.is_valid() and profile_form.is_valid() \
and not any([not form.is_valid() for form in event_forms]): if user_form.is_valid():
member = user_form.save() member = user_form.save()
(profile, _) = CofProfile.objects.get_or_create(user=member) profile, _ = CofProfile.objects.get_or_create(user=member)
was_cof = profile.is_cof was_cof = profile.is_cof
request_dict["num"] = profile.num request_dict["num"] = profile.num
# Maintenant on remplit le formulaire de profil
profile_form = RegistrationProfileForm(request_dict, profile_form = RegistrationProfileForm(request_dict,
instance=profile) instance=profile)
profile_form.is_valid() if (profile_form.is_valid() and event_formset.is_valid()
profile_form.save() and clubs_form.is_valid()):
(profile, _) = CofProfile.objects.get_or_create(user=member) # Enregistrement du profil
if profile.is_cof and not was_cof: profile = profile_form.save()
send_custom_mail(member, "bienvenue") if profile.is_cof and not was_cof:
for form in event_forms: send_custom_mail(member, "bienvenue")
if form.cleaned_data['status'] == 'no': # Enregistrement des inscriptions aux événements
try: for form in event_formset:
current_registration = EventRegistration.objects.get( if 'status' not in form.cleaned_data:
form.cleaned_data['status'] = 'no'
if form.cleaned_data['status'] == 'no':
try:
current_registration = EventRegistration.objects \
.get(user=member, event=form.event)
current_registration.delete()
except EventRegistration.DoesNotExist:
pass
continue
all_choices = get_event_form_choices(form.event, form)
(current_registration, created_reg) = \
EventRegistration.objects.get_or_create(
user=member, event=form.event) user=member, event=form.event)
current_registration.delete() update_event_form_comments(form.event, form,
except EventRegistration.DoesNotExist: current_registration)
pass current_registration.options = all_choices
continue current_registration.paid = \
all_choices = get_event_form_choices(form.event, form) (form.cleaned_data['status'] == 'paid')
(current_registration, created_reg) = \ current_registration.save()
EventRegistration.objects.get_or_create(user=member, if form.event.title == "Mega 15" and created_reg:
event=form.event) field = EventCommentField.objects.get(
update_event_form_comments(form.event, form, event=form.event, name="Commentaires")
current_registration) try:
current_registration.options = all_choices comments = EventCommentValue.objects.get(
current_registration.paid = \ commentfield=field,
(form.cleaned_data['status'] == 'paid') registration=current_registration).content
current_registration.save() except EventCommentValue.DoesNotExist:
if form.event.title == "Mega 15" and created_reg: comments = field.default
field = EventCommentField.objects.get(event=form.event, send_custom_mail(member, "mega",
name="Commentaires") {"remarques": comments})
try: # Enregistrement des inscriptions aux clubs
comments = EventCommentValue.objects.get( member.clubs.clear()
commentfield=field, for club in clubs_form.cleaned_data['clubs']:
registration=current_registration).content club.membres.add(member)
except EventCommentValue.DoesNotExist: club.save()
comments = field.default success = True
send_custom_mail(member, "mega", {"remarques": comments})
success = True
return render(request, "registration_post.html", return render(request, "registration_post.html",
{"success": success, {"success": success,
"user_form": user_form, "user_form": user_form,
"profile_form": profile_form, "profile_form": profile_form,
"member": member, "member": member,
"login_clipper": login_clipper, "login_clipper": login_clipper,
"event_forms": event_forms}) "event_formset": event_formset,
"clubs_form": clubs_form})
else: else:
return render(request, "registration.html") return render(request, "registration.html")
# -----
# Clubs
# -----
@login_required
def membres_club(request, name):
# Vérification des permissions : l'utilisateur doit être membre du burô
# ou respo du club.
user = request.user
club = get_object_or_404(Club, name=name)
if not request.user.profile.is_buro \
and club not in user.clubs_geres.all():
return HttpResponseForbidden('<h1>Permission denied</h1>')
members_no_respo = club.membres.exclude(clubs_geres=club).all()
return render(request, 'membres_clubs.html',
{'club': club,
'members_no_respo': members_no_respo})
@buro_required
def change_respo(request, club_name, user_id):
club = get_object_or_404(Club, name=club_name)
user = get_object_or_404(User, id=user_id)
if user in club.respos.all():
club.respos.remove(user)
elif user in club.membres.all():
club.respos.add(user)
else:
raise Http404
return redirect('membres-club', name=club_name)
@cof_required
def liste_clubs(request):
clubs = Club.objects
if request.user.profile.is_buro:
data = {'owned_clubs': clubs.all()}
else:
data = {'owned_clubs': request.user.clubs_geres,
'other_clubs': clubs.exclude(respos=request.user)}
return render(request, 'liste_clubs.html', data)
@buro_required @buro_required
def export_members(request): def export_members(request):
response = HttpResponse(content_type='text/csv') response = HttpResponse(content_type='text/csv')

View file

@ -1,4 +1,4 @@
# Doit être lancé par bootstrap.sh # Doit être lancé par bootstrap.sh
python manage.py migrate python manage.py migrate
python manage.py loaddata users root bda gestion python manage.py loaddata users root bda gestion sites