core -- Apply black + isort to all files

This commit is contained in:
Aurélien Delobelle 2018-10-06 12:35:49 +02:00
parent 104e71dcf6
commit fdd2b35289
196 changed files with 10727 additions and 8365 deletions

View file

@ -1 +1 @@
default_app_config = 'gestioncof.apps.GestioncofConfig'
default_app_config = "gestioncof.apps.GestioncofConfig"

View file

@ -1,23 +1,35 @@
from dal.autocomplete import ModelSelect2
from django import forms
from django.contrib import admin
from django.utils.translation import ugettext_lazy as _
from gestioncof.models import SurveyQuestionAnswer, SurveyQuestion, \
CofProfile, EventOption, EventOptionChoice, Event, Club, \
Survey, EventCommentField, EventRegistration
from gestioncof.petits_cours_models import PetitCoursDemande, \
PetitCoursSubject, PetitCoursAbility, PetitCoursAttribution, \
PetitCoursAttributionCounter
from django.contrib.auth.models import User, Group, Permission
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import Group, Permission, User
from django.core.urlresolvers import reverse
from django.utils.safestring import mark_safe
from django.db.models import Q
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from dal.autocomplete import ModelSelect2
from gestioncof.models import (
Club,
CofProfile,
Event,
EventCommentField,
EventOption,
EventOptionChoice,
EventRegistration,
Survey,
SurveyQuestion,
SurveyQuestionAnswer,
)
from gestioncof.petits_cours_models import (
PetitCoursAbility,
PetitCoursAttribution,
PetitCoursAttributionCounter,
PetitCoursDemande,
PetitCoursSubject,
)
def add_link_field(target_model='', field='', link_text=str,
desc_text=str):
def add_link_field(target_model="", field="", link_text=str, desc_text=str):
def add_link(cls):
reverse_name = target_model or cls.model.__name__.lower()
@ -28,14 +40,14 @@ def add_link_field(target_model='', field='', link_text=str,
if not link_obj.id:
return ""
url = reverse(reverse_path, args=(link_obj.id,))
return mark_safe("<a href='%s'>%s</a>"
% (url, link_text(link_obj)))
return mark_safe("<a href='%s'>%s</a>" % (url, link_text(link_obj)))
link.allow_tags = True
link.short_description = desc_text(reverse_name + ' link')
link.short_description = desc_text(reverse_name + " link")
cls.link = link
cls.readonly_fields =\
list(getattr(cls, 'readonly_fields', [])) + ['link']
cls.readonly_fields = list(getattr(cls, "readonly_fields", [])) + ["link"]
return cls
return add_link
@ -43,32 +55,28 @@ class SurveyQuestionAnswerInline(admin.TabularInline):
model = SurveyQuestionAnswer
@add_link_field(desc_text=lambda x: "Réponses",
link_text=lambda x: "Éditer les réponses")
@add_link_field(
desc_text=lambda x: "Réponses", link_text=lambda x: "Éditer les réponses"
)
class SurveyQuestionInline(admin.TabularInline):
model = SurveyQuestion
class SurveyQuestionAdmin(admin.ModelAdmin):
search_fields = ('survey__title', 'answer')
inlines = [
SurveyQuestionAnswerInline,
]
search_fields = ("survey__title", "answer")
inlines = [SurveyQuestionAnswerInline]
class SurveyAdmin(admin.ModelAdmin):
search_fields = ('title', 'details')
inlines = [
SurveyQuestionInline,
]
search_fields = ("title", "details")
inlines = [SurveyQuestionInline]
class EventOptionChoiceInline(admin.TabularInline):
model = EventOptionChoice
@add_link_field(desc_text=lambda x: "Choix",
link_text=lambda x: "Éditer les choix")
@add_link_field(desc_text=lambda x: "Choix", link_text=lambda x: "Éditer les choix")
class EventOptionInline(admin.TabularInline):
model = EventOption
@ -78,18 +86,13 @@ class EventCommentFieldInline(admin.TabularInline):
class EventOptionAdmin(admin.ModelAdmin):
search_fields = ('event__title', 'name')
inlines = [
EventOptionChoiceInline,
]
search_fields = ("event__title", "name")
inlines = [EventOptionChoiceInline]
class EventAdmin(admin.ModelAdmin):
search_fields = ('title', 'location', 'description')
inlines = [
EventOptionInline,
EventCommentFieldInline,
]
search_fields = ("title", "location", "description")
inlines = [EventOptionInline, EventCommentFieldInline]
class CofProfileInline(admin.StackedInline):
@ -98,10 +101,9 @@ class CofProfileInline(admin.StackedInline):
class FkeyLookup(object):
def __init__(self, fkeydecl, short_description=None,
admin_order_field=None):
self.fk, fkattrs = fkeydecl.split('__', 1)
self.fkattrs = fkattrs.split('__')
def __init__(self, fkeydecl, short_description=None, admin_order_field=None):
self.fk, fkattrs = fkeydecl.split("__", 1)
self.fkattrs = fkattrs.split("__")
self.short_description = short_description or self.fkattrs[-1]
self.admin_order_field = admin_order_field or fkeydecl
@ -126,19 +128,19 @@ def ProfileInfo(field, short_description, boolean=False):
return getattr(self.profile, field)
except CofProfile.DoesNotExist:
return ""
getter.short_description = short_description
getter.boolean = boolean
return getter
User.profile_login_clipper = FkeyLookup("profile__login_clipper",
"Login clipper")
User.profile_login_clipper = FkeyLookup("profile__login_clipper", "Login clipper")
User.profile_phone = ProfileInfo("phone", "Téléphone")
User.profile_occupation = ProfileInfo("occupation", "Occupation")
User.profile_departement = ProfileInfo("departement", "Departement")
User.profile_mailing_cof = ProfileInfo("mailing_cof", "ML COF", True)
User.profile_mailing_bda = ProfileInfo("mailing_bda", "ML BdA", True)
User.profile_mailing_bda_revente = ProfileInfo("mailing_bda_revente",
"ML BdA-R", True)
User.profile_mailing_bda_revente = ProfileInfo("mailing_bda_revente", "ML BdA-R", True)
class UserProfileAdmin(UserAdmin):
@ -147,7 +149,8 @@ class UserProfileAdmin(UserAdmin):
return obj.profile.is_buro
except CofProfile.DoesNotExist:
return False
is_buro.short_description = 'Membre du Buro'
is_buro.short_description = "Membre du Buro"
is_buro.boolean = True
def is_cof(self, obj):
@ -155,27 +158,33 @@ class UserProfileAdmin(UserAdmin):
return obj.profile.is_cof
except CofProfile.DoesNotExist:
return False
is_cof.short_description = 'Membre du COF'
is_cof.short_description = "Membre du COF"
is_cof.boolean = True
list_display = (
UserAdmin.list_display
+ ('profile_login_clipper', 'profile_phone', 'profile_occupation',
'profile_mailing_cof', 'profile_mailing_bda',
'profile_mailing_bda_revente', 'is_cof', 'is_buro', )
list_display = UserAdmin.list_display + (
"profile_login_clipper",
"profile_phone",
"profile_occupation",
"profile_mailing_cof",
"profile_mailing_bda",
"profile_mailing_bda_revente",
"is_cof",
"is_buro",
)
list_display_links = ('username', 'email', 'first_name', 'last_name')
list_filter = UserAdmin.list_filter \
+ ('profile__is_cof', 'profile__is_buro', 'profile__mailing_cof',
'profile__mailing_bda')
search_fields = UserAdmin.search_fields + ('profile__phone',)
inlines = [
CofProfileInline,
]
list_display_links = ("username", "email", "first_name", "last_name")
list_filter = UserAdmin.list_filter + (
"profile__is_cof",
"profile__is_buro",
"profile__mailing_cof",
"profile__mailing_bda",
)
search_fields = UserAdmin.search_fields + ("profile__phone",)
inlines = [CofProfileInline]
staff_fieldsets = [
(None, {'fields': ['username', 'password']}),
(_('Personal info'), {'fields': ['first_name', 'last_name', 'email']}),
(None, {"fields": ["username", "password"]}),
(_("Personal info"), {"fields": ["first_name", "last_name", "email"]}),
]
def get_fieldsets(self, request, user=None):
@ -184,15 +193,15 @@ class UserProfileAdmin(UserAdmin):
return super().get_fieldsets(request, user)
def save_model(self, request, user, form, change):
cof_group, created = Group.objects.get_or_create(name='COF')
cof_group, created = Group.objects.get_or_create(name="COF")
if created:
# Si le groupe COF n'était pas déjà dans la bdd
# On lui assigne les bonnes permissions
perms = Permission.objects.filter(
Q(content_type__app_label='gestioncof')
| Q(content_type__app_label='bda')
| (Q(content_type__app_label='auth')
& Q(content_type__model='user')))
Q(content_type__app_label="gestioncof")
| Q(content_type__app_label="bda")
| (Q(content_type__app_label="auth") & Q(content_type__model="user"))
)
cof_group.permissions = perms
# On y associe les membres du Burô
cof_group.user_set = User.objects.filter(profile__is_buro=True)
@ -214,72 +223,97 @@ def user_str(self):
return "{} ({})".format(self.get_full_name(), self.username)
else:
return self.username
User.__str__ = user_str
class EventRegistrationAdminForm(forms.ModelForm):
class Meta:
widgets = {
'user': ModelSelect2(url='cof-user-autocomplete'),
}
widgets = {"user": ModelSelect2(url="cof-user-autocomplete")}
class EventRegistrationAdmin(admin.ModelAdmin):
form = EventRegistrationAdminForm
list_display = ('__str__', 'event', 'user', 'paid')
list_filter = ('paid',)
search_fields = ('user__username', 'user__first_name', 'user__last_name',
'user__email', 'event__title')
list_display = ("__str__", "event", "user", "paid")
list_filter = ("paid",)
search_fields = (
"user__username",
"user__first_name",
"user__last_name",
"user__email",
"event__title",
)
class PetitCoursAbilityAdmin(admin.ModelAdmin):
list_display = ('user', 'matiere', 'niveau', 'agrege')
search_fields = ('user__username', 'user__first_name', 'user__last_name',
'user__email', 'matiere__name', 'niveau')
list_filter = ('matiere', 'niveau', 'agrege')
list_display = ("user", "matiere", "niveau", "agrege")
search_fields = (
"user__username",
"user__first_name",
"user__last_name",
"user__email",
"matiere__name",
"niveau",
)
list_filter = ("matiere", "niveau", "agrege")
class PetitCoursAttributionAdmin(admin.ModelAdmin):
list_display = ('user', 'demande', 'matiere', 'rank', )
search_fields = ('user__username', 'matiere__name')
list_display = ("user", "demande", "matiere", "rank")
search_fields = ("user__username", "matiere__name")
class PetitCoursAttributionCounterAdmin(admin.ModelAdmin):
list_display = ('user', 'matiere', 'count', )
list_filter = ('matiere',)
search_fields = ('user__username', 'user__first_name', 'user__last_name',
'user__email', 'matiere__name')
actions = ['reset', ]
list_display = ("user", "matiere", "count")
list_filter = ("matiere",)
search_fields = (
"user__username",
"user__first_name",
"user__last_name",
"user__email",
"matiere__name",
)
actions = ["reset"]
actions_on_bottom = True
def reset(self, request, queryset):
queryset.update(count=0)
reset.short_description = "Remise à zéro du compteur"
class PetitCoursDemandeAdmin(admin.ModelAdmin):
list_display = ('name', 'email', 'agrege_requis', 'niveau', 'created',
'traitee', 'processed')
list_filter = ('traitee', 'niveau')
search_fields = ('name', 'email', 'phone', 'lieu', 'remarques')
list_display = (
"name",
"email",
"agrege_requis",
"niveau",
"created",
"traitee",
"processed",
)
list_filter = ("traitee", "niveau")
search_fields = ("name", "email", "phone", "lieu", "remarques")
class ClubAdminForm(forms.ModelForm):
def clean(self):
cleaned_data = super().clean()
respos = cleaned_data.get('respos')
members = cleaned_data.get('membres')
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())
% respo.get_full_name()
)
return cleaned_data
class ClubAdmin(admin.ModelAdmin):
list_display = ['name']
list_display = ["name"]
form = ClubAdminForm
@ -294,7 +328,6 @@ admin.site.register(Club, ClubAdmin)
admin.site.register(PetitCoursSubject)
admin.site.register(PetitCoursAbility, PetitCoursAbilityAdmin)
admin.site.register(PetitCoursAttribution, PetitCoursAttributionAdmin)
admin.site.register(PetitCoursAttributionCounter,
PetitCoursAttributionCounterAdmin)
admin.site.register(PetitCoursAttributionCounter, PetitCoursAttributionCounterAdmin)
admin.site.register(PetitCoursDemande, PetitCoursDemandeAdmin)
admin.site.register(EventRegistration, EventRegistrationAdmin)

View file

@ -2,14 +2,16 @@ from django.apps import AppConfig
class GestioncofConfig(AppConfig):
name = 'gestioncof'
name = "gestioncof"
verbose_name = "Gestion des adhérents du COF"
def ready(self):
from . import signals
self.register_config()
def register_config(self):
import djconfig
from .forms import GestioncofConfigForm
djconfig.register(GestioncofConfigForm)

View file

@ -1,13 +1,12 @@
from django import shortcuts
from django.conf import settings
from django.contrib.auth.models import User
from django.db.models import Q
from django.http import Http404
from ldap3 import Connection
from django import shortcuts
from django.http import Http404
from django.db.models import Q
from django.contrib.auth.models import User
from django.conf import settings
from gestioncof.models import CofProfile
from gestioncof.decorators import buro_required
from gestioncof.models import CofProfile
class Clipper(object):
@ -20,74 +19,70 @@ class Clipper(object):
self.fullname = fullname
def __str__(self):
return '{} ({})'.format(self.clipper, self.fullname)
return "{} ({})".format(self.clipper, self.fullname)
def __eq__(self, other):
return (
self.clipper == other.clipper and self.fullname == other.fullname)
return self.clipper == other.clipper and self.fullname == other.fullname
@buro_required
def autocomplete(request):
if "q" not in request.GET:
raise Http404
q = request.GET['q']
data = {
'q': q,
}
q = request.GET["q"]
data = {"q": q}
queries = {}
bits = q.split()
# Fetching data from User and CofProfile tables
queries['members'] = CofProfile.objects.filter(is_cof=True)
queries['users'] = User.objects.filter(profile__is_cof=False)
queries["members"] = CofProfile.objects.filter(is_cof=True)
queries["users"] = User.objects.filter(profile__is_cof=False)
for bit in bits:
queries['members'] = queries['members'].filter(
queries["members"] = queries["members"].filter(
Q(user__first_name__icontains=bit)
| Q(user__last_name__icontains=bit)
| Q(user__username__icontains=bit)
| Q(login_clipper__icontains=bit))
queries['users'] = queries['users'].filter(
| Q(login_clipper__icontains=bit)
)
queries["users"] = queries["users"].filter(
Q(first_name__icontains=bit)
| Q(last_name__icontains=bit)
| Q(username__icontains=bit))
queries['members'] = queries['members'].distinct()
queries['users'] = queries['users'].distinct()
| Q(username__icontains=bit)
)
queries["members"] = queries["members"].distinct()
queries["users"] = queries["users"].distinct()
# Clearing redundancies
usernames = (
set(queries['members'].values_list('login_clipper', flat='True'))
| set(queries['users'].values_list('profile__login_clipper',
flat='True'))
usernames = set(queries["members"].values_list("login_clipper", flat="True")) | set(
queries["users"].values_list("profile__login_clipper", flat="True")
)
# Fetching data from the SPI
if getattr(settings, 'LDAP_SERVER_URL', None):
if getattr(settings, "LDAP_SERVER_URL", None):
# Fetching
ldap_query = '(&{:s})'.format(''.join(
'(|(cn=*{bit:s}*)(uid=*{bit:s}*))'.format(bit=bit)
for bit in bits if bit.isalnum()
))
ldap_query = "(&{:s})".format(
"".join(
"(|(cn=*{bit:s}*)(uid=*{bit:s}*))".format(bit=bit)
for bit in bits
if bit.isalnum()
)
)
if ldap_query != "(&)":
# If none of the bits were legal, we do not perform the query
entries = None
with Connection(settings.LDAP_SERVER_URL) as conn:
conn.search(
'dc=spi,dc=ens,dc=fr', ldap_query,
attributes=['uid', 'cn']
)
conn.search("dc=spi,dc=ens,dc=fr", ldap_query, attributes=["uid", "cn"])
entries = conn.entries
# Clearing redundancies
queries['clippers'] = [
queries["clippers"] = [
Clipper(entry.uid.value, entry.cn.value)
for entry in entries
if entry.uid.value
and entry.uid.value not in usernames
if entry.uid.value and entry.uid.value not in usernames
]
# Resulting data
data.update(queries)
data['options'] = sum(len(query) for query in queries)
data["options"] = sum(len(query) for query in queries)
return shortcuts.render(request, "autocomplete_user.html", data)

View file

@ -1,14 +1,16 @@
import csv
from django.apps import apps
from django.http import HttpResponse, HttpResponseForbidden
from django.template.defaultfilters import slugify
from django.apps import apps
def export(qs, fields=None):
model = qs.model
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename=%s.csv' \
% slugify(model.__name__)
response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = "attachment; filename=%s.csv" % slugify(
model.__name__
)
writer = csv.writer(response)
# Write headers to CSV file
if fields:
@ -32,8 +34,9 @@ def export(qs, fields=None):
return response
def admin_list_export(request, model_name, app_label, queryset=None,
fields=None, list_display=True):
def admin_list_export(
request, model_name, app_label, queryset=None, fields=None, list_display=True
):
"""
Put the following line in your urls.py BEFORE your admin include
(r'^admin/(?P<app_label>[\d\w]+)/(?P<model_name>[\d\w]+)/csv/',

View file

@ -8,6 +8,7 @@ def is_cof(user):
except:
return False
cof_required = user_passes_test(is_cof)
@ -18,4 +19,5 @@ def is_buro(user):
except:
return False
buro_required = user_passes_test(is_buro)

View file

@ -1,16 +1,13 @@
from django import forms
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import User
from django.forms.widgets import RadioSelect, CheckboxSelectMultiple
from django.forms.formsets import BaseFormSet, formset_factory
from django.forms.widgets import CheckboxSelectMultiple, RadioSelect
from django.utils.translation import ugettext_lazy as _
from djconfig.forms import ConfigForm
from gestioncof.models import CofProfile, EventCommentValue, \
CalendarSubscription, Club
from gestioncof.widgets import TriStateCheckbox
from bda.models import Spectacle
from gestioncof.models import CalendarSubscription, Club, CofProfile, EventCommentValue
from gestioncof.widgets import TriStateCheckbox
class EventForm(forms.Form):
@ -28,31 +25,33 @@ class EventForm(forms.Form):
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()]
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]
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)
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)
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_'):
if name.startswith("option_"):
yield (self.fields[name].option_id, value)
@ -69,31 +68,33 @@ class SurveyForm(forms.Form):
else:
answers[answer.survey_question.id].append(answer.id)
for question in survey.questions.all():
choices = [(answer.id, answer.answer)
for answer in question.answers.all()]
choices = [(answer.id, answer.answer) for answer in question.answers.all()]
if question.multi_answers:
initial = [] if question.id not in answers\
else answers[question.id]
initial = [] if question.id not in answers else answers[question.id]
field = forms.MultipleChoiceField(
label=question.question,
choices=choices,
widget=CheckboxSelectMultiple,
required=False,
initial=initial)
initial=initial,
)
else:
initial = None if question.id not in answers\
else answers[question.id][0]
field = forms.ChoiceField(label=question.question,
choices=choices,
widget=RadioSelect,
required=False,
initial=initial)
initial = (
None if question.id not in answers else answers[question.id][0]
)
field = forms.ChoiceField(
label=question.question,
choices=choices,
widget=RadioSelect,
required=False,
initial=initial,
)
field.question_id = question.id
self.fields["question_%d" % question.id] = field
def answers(self):
for name, value in self.cleaned_data.items():
if name.startswith('question_'):
if name.startswith("question_"):
yield (self.fields[name].question_id, value)
@ -104,8 +105,7 @@ class SurveyStatusFilterForm(forms.Form):
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):
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"
@ -114,16 +114,20 @@ class SurveyStatusFilterForm(forms.Form):
choices=[("yes", "yes"), ("no", "no"), ("none", "none")],
widget=TriStateCheckbox,
required=False,
initial=initial)
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)
if name.startswith("question_"):
yield (
self.fields[name].question_id,
self.fields[name].answer_id,
value,
)
class EventStatusFilterForm(forms.Form):
@ -133,8 +137,7 @@ class EventStatusFilterForm(forms.Form):
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):
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"
@ -143,7 +146,8 @@ class EventStatusFilterForm(forms.Form):
choices=[("yes", "yes"), ("no", "no"), ("none", "none")],
widget=TriStateCheckbox,
required=False,
initial=initial)
initial=initial,
)
field.option_id = option.id
field.choice_id = choice.id
self.fields[name] = field
@ -153,19 +157,19 @@ class EventStatusFilterForm(forms.Form):
initial = self.data.get(self.add_prefix(name), None)
else:
initial = "none"
field = forms.ChoiceField(label="Événement payé",
choices=[("yes", "yes"), ("no", "no"),
("none", "none")],
widget=TriStateCheckbox,
required=False,
initial=initial)
field = forms.ChoiceField(
label="Événement payé",
choices=[("yes", "yes"), ("no", "no"), ("none", "none")],
widget=TriStateCheckbox,
required=False,
initial=initial,
)
self.fields[name] = field
def filters(self):
for name, value in self.cleaned_data.items():
if name.startswith('option_'):
yield (self.fields[name].option_id,
self.fields[name].choice_id, value)
if name.startswith("option_"):
yield (self.fields[name].option_id, self.fields[name].choice_id, value)
elif name == "event_has_paid":
yield ("has_paid", None, value)
@ -184,14 +188,14 @@ class ProfileForm(forms.ModelForm):
"mailing_cof",
"mailing_bda",
"mailing_bda_revente",
"mailing_unernestaparis"
"mailing_unernestaparis",
]
class RegistrationUserForm(forms.ModelForm):
def __init__(self, *args, **kw):
super().__init__(*args, **kw)
self.fields['username'].help_text = ""
self.fields["username"].help_text = ""
class Meta:
model = User
@ -202,22 +206,23 @@ 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)
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']
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.'))
raise forms.ValidationError(_("Mots de passe non identiques."))
return pass2
def save(self, commit=True, *args, **kwargs):
user = super().save(commit, *args, **kwargs)
user.set_password(self.cleaned_data['password2'])
user.set_password(self.cleaned_data["password2"])
if commit:
user.save()
return user
@ -226,48 +231,62 @@ class RegistrationPassUserForm(RegistrationUserForm):
class RegistrationProfileForm(forms.ModelForm):
def __init__(self, *args, **kw):
super().__init__(*args, **kw)
self.fields['mailing_cof'].initial = True
self.fields['mailing_bda'].initial = True
self.fields['mailing_bda_revente'].initial = True
self.fields['mailing_unernestaparis'].initial = True
self.fields["mailing_cof"].initial = True
self.fields["mailing_bda"].initial = True
self.fields["mailing_bda_revente"].initial = True
self.fields["mailing_unernestaparis"].initial = True
self.fields.keyOrder = [
'login_clipper',
'phone',
'occupation',
'departement',
'is_cof',
'type_cotiz',
'mailing_cof',
'mailing_bda',
'mailing_bda_revente',
"login_clipper",
"phone",
"occupation",
"departement",
"is_cof",
"type_cotiz",
"mailing_cof",
"mailing_bda",
"mailing_bda_revente",
"mailing_unernestaparis",
'comments'
"comments",
]
class Meta:
model = CofProfile
fields = ("login_clipper", "phone", "occupation",
"departement", "is_cof", "type_cotiz", "mailing_cof",
"mailing_bda", "mailing_bda_revente",
"mailing_unernestaparis", "comments")
fields = (
"login_clipper",
"phone",
"occupation",
"departement",
"is_cof",
"type_cotiz",
"mailing_cof",
"mailing_bda",
"mailing_bda_revente",
"mailing_unernestaparis",
"comments",
)
STATUS_CHOICES = (('no', 'Non'),
('wait', 'Oui mais attente paiement'),
('paid', 'Oui payé'),)
STATUS_CHOICES = (
("no", "Non"),
("wait", "Oui mais attente paiement"),
("paid", "Oui payé"),
)
class AdminEventForm(forms.Form):
status = forms.ChoiceField(label="Inscription", initial="no",
choices=STATUS_CHOICES, widget=RadioSelect)
status = forms.ChoiceField(
label="Inscription", initial="no", choices=STATUS_CHOICES, widget=RadioSelect
)
def __init__(self, *args, **kwargs):
self.event = kwargs.pop("event")
registration = kwargs.pop("current_registration", None)
current_choices, paid = \
(registration.options.all(), registration.paid) \
if registration is not None else ([], None)
current_choices, paid = (
(registration.options.all(), registration.paid)
if registration is not None
else ([], None)
)
if paid is True:
kwargs["initial"] = {"status": "paid"}
elif paid is False:
@ -283,66 +302,69 @@ class AdminEventForm(forms.Form):
choices[choice.event_option.id].append(choice.id)
all_choices = choices
for option in self.event.options.all():
choices = [(choice.id, choice.value)
for choice in option.choices.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]
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)
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)
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
for commentfield in self.event.commentfields.all():
initial = commentfield.default
if registration is not None:
try:
initial = registration.comments \
.get(commentfield=commentfield).content
initial = registration.comments.get(
commentfield=commentfield
).content
except EventCommentValue.DoesNotExist:
pass
widget = forms.Textarea if commentfield.fieldtype == "text" \
else forms.TextInput
field = forms.CharField(label=commentfield.name,
widget=widget,
required=False,
initial=initial)
widget = (
forms.Textarea if commentfield.fieldtype == "text" else forms.TextInput
)
field = forms.CharField(
label=commentfield.name, widget=widget, required=False, initial=initial
)
field.comment_id = commentfield.id
self.fields["comment_%d" % commentfield.id] = field
def choices(self):
for name, value in self.cleaned_data.items():
if name.startswith('option_'):
if name.startswith("option_"):
yield (self.fields[name].option_id, value)
def comments(self):
for name, value in self.cleaned_data.items():
if name.startswith('comment_'):
if name.startswith("comment_"):
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.events = kwargs.pop("events")
self.current_registrations = kwargs.pop("current_registrations", None)
self.extra = len(self.events)
super().__init__(*args, **kwargs)
def _construct_form(self, index, **kwargs):
kwargs['event'] = self.events[index]
kwargs["event"] = self.events[index]
if self.current_registrations is not None:
kwargs['current_registration'] = self.current_registrations[index]
kwargs["current_registration"] = self.current_registrations[index]
return super()._construct_form(index, **kwargs)
@ -351,34 +373,36 @@ EventFormset = formset_factory(AdminEventForm, BaseEventRegistrationFormset)
class CalendarForm(forms.ModelForm):
subscribe_to_events = forms.BooleanField(
initial=True,
label="Événements du COF",
required=False)
initial=True, label="Événements du COF", required=False
)
subscribe_to_my_shows = forms.BooleanField(
initial=True,
label="Les spectacles pour lesquels j'ai obtenu une place",
required=False)
initial=True,
label="Les spectacles pour lesquels j'ai obtenu une place",
required=False,
)
other_shows = forms.ModelMultipleChoiceField(
label="Spectacles supplémentaires",
queryset=Spectacle.objects.filter(tirage__active=True),
widget=forms.CheckboxSelectMultiple,
required=False)
label="Spectacles supplémentaires",
queryset=Spectacle.objects.filter(tirage__active=True),
widget=forms.CheckboxSelectMultiple,
required=False,
)
class Meta:
model = CalendarSubscription
fields = ['subscribe_to_events', 'subscribe_to_my_shows',
'other_shows']
fields = ["subscribe_to_events", "subscribe_to_my_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)
label="Inscriptions aux clubs du COF",
queryset=Club.objects.all(),
widget=forms.CheckboxSelectMultiple,
required=False,
)
# ---
@ -386,9 +410,10 @@ class ClubsForm(forms.Form):
# TODO: move this to the `gestion` app once the supportBDS branch is merged
# ---
class GestioncofConfigForm(ConfigForm):
gestion_banner = forms.CharField(
label=_("Announcements banner"),
help_text=_("An empty banner disables annoucements"),
max_length=2048
max_length=2048,
)

View file

@ -2,8 +2,8 @@
Un mixin à utiliser avec BaseCommand pour charger des objets depuis un json
"""
import os
import json
import os
from django.core.management.base import BaseCommand
@ -13,15 +13,14 @@ class MyBaseCommand(BaseCommand):
Ajoute une méthode ``from_json`` qui charge des objets à partir d'un json.
"""
def from_json(self, filename, data_dir, klass,
callback=lambda obj: obj):
def from_json(self, filename, data_dir, klass, callback=lambda obj: obj):
"""
Charge les objets contenus dans le fichier json référencé par
``filename`` dans la base de donnée. La fonction callback est appelées
sur chaque objet avant enregistrement.
"""
self.stdout.write("Chargement de {:s}".format(filename))
with open(os.path.join(data_dir, filename), 'r') as file:
with open(os.path.join(data_dir, filename), "r") as file:
descriptions = json.load(file)
objects = []
nb_new = 0
@ -36,6 +35,7 @@ class MyBaseCommand(BaseCommand):
objects.append(obj)
nb_new += 1
self.stdout.write("- {:d} objets créés".format(nb_new))
self.stdout.write("- {:d} objets gardés en l'état"
.format(len(objects)-nb_new))
self.stdout.write(
"- {:d} objets gardés en l'état".format(len(objects) - nb_new)
)
return objects

View file

@ -15,13 +15,14 @@ from django.core.management import call_command
from gestioncof.management.base import MyBaseCommand
from gestioncof.petits_cours_models import (
PetitCoursAbility, PetitCoursSubject, LEVELS_CHOICES,
PetitCoursAttributionCounter
LEVELS_CHOICES,
PetitCoursAbility,
PetitCoursAttributionCounter,
PetitCoursSubject,
)
# Où sont stockés les fichiers json
DATA_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)),
'data')
DATA_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "data")
class Command(MyBaseCommand):
@ -32,11 +33,11 @@ class Command(MyBaseCommand):
Permet de ne pas créer l'utilisateur "root".
"""
parser.add_argument(
'--no-root',
action='store_true',
dest='no-root',
"--no-root",
action="store_true",
dest="no-root",
default=False,
help='Ne crée pas l\'utilisateur "root"'
help='Ne crée pas l\'utilisateur "root"',
)
def handle(self, *args, **options):
@ -45,24 +46,25 @@ class Command(MyBaseCommand):
# ---
# Gaulois
gaulois = self.from_json('gaulois.json', DATA_DIR, User)
gaulois = self.from_json("gaulois.json", DATA_DIR, User)
for user in gaulois:
user.profile.is_cof = True
user.profile.save()
# Romains
self.from_json('romains.json', DATA_DIR, User)
self.from_json("romains.json", DATA_DIR, User)
# Root
no_root = options.get('no-root', False)
no_root = options.get("no-root", False)
if not no_root:
self.stdout.write("Création de l'utilisateur root")
root, _ = User.objects.get_or_create(
username='root',
first_name='super',
last_name='user',
email='root@localhost')
root.set_password('root')
username="root",
first_name="super",
last_name="user",
email="root@localhost",
)
root.set_password("root")
root.is_staff = True
root.is_superuser = True
root.profile.is_cof = True
@ -87,18 +89,17 @@ class Command(MyBaseCommand):
# L'utilisateur est compétent dans une matière
subject = random.choice(subjects)
if not PetitCoursAbility.objects.filter(
user=user,
matiere=subject).exists():
user=user, matiere=subject
).exists():
PetitCoursAbility.objects.create(
user=user,
matiere=subject,
niveau=random.choice(levels),
agrege=bool(random.randint(0, 1))
agrege=bool(random.randint(0, 1)),
)
# On initialise son compteur d'attributions
PetitCoursAttributionCounter.objects.get_or_create(
user=user,
matiere=subject
user=user, matiere=subject
)
self.stdout.write("- {:d} inscriptions".format(nb_of_teachers))
@ -106,10 +107,10 @@ class Command(MyBaseCommand):
# Le BdA
# ---
call_command('loadbdadevdata')
call_command("loadbdadevdata")
# ---
# La K-Fêt
# ---
call_command('loadkfetdevdata')
call_command("loadkfetdevdata")

View file

@ -4,11 +4,10 @@ Import des mails de GestioCOF dans la base de donnée
import json
import os
from custommail.models import Type, CustomMail, Variable
from django.core.management.base import BaseCommand
from custommail.models import CustomMail, Type, Variable
from django.contrib.contenttypes.models import ContentType
from django.core.management.base import BaseCommand
DATA_LOCATION = os.path.join(os.path.dirname(__file__), "..", "data", "custommail.json")
@ -19,15 +18,15 @@ def dummy_log(__):
# XXX. this should probably be in the custommail package
def load_from_file(log=dummy_log, verbosity=1):
with open(DATA_LOCATION, 'r') as jsonfile:
with open(DATA_LOCATION, "r") as jsonfile:
mail_data = json.load(jsonfile)
# On se souvient à quel objet correspond quel pk du json
assoc = {'types': {}, 'mails': {}}
status = {'synced': 0, 'unchanged': 0}
assoc = {"types": {}, "mails": {}}
status = {"synced": 0, "unchanged": 0}
for obj in mail_data:
fields = obj['fields']
fields = obj["fields"]
# Pour les trois types d'objets :
# - On récupère les objets référencés par les clefs étrangères
@ -36,58 +35,55 @@ def load_from_file(log=dummy_log, verbosity=1):
# plus haut
# Variable types
if obj['model'] == 'custommail.variabletype':
fields['inner1'] = assoc['types'].get(fields['inner1'])
fields['inner2'] = assoc['types'].get(fields['inner2'])
if fields['kind'] == 'model':
fields['content_type'] = (
ContentType.objects
.get_by_natural_key(*fields['content_type'])
if obj["model"] == "custommail.variabletype":
fields["inner1"] = assoc["types"].get(fields["inner1"])
fields["inner2"] = assoc["types"].get(fields["inner2"])
if fields["kind"] == "model":
fields["content_type"] = ContentType.objects.get_by_natural_key(
*fields["content_type"]
)
var_type, _ = Type.objects.get_or_create(**fields)
assoc['types'][obj['pk']] = var_type
assoc["types"][obj["pk"]] = var_type
# Custom mails
if obj['model'] == 'custommail.custommail':
if obj["model"] == "custommail.custommail":
mail = None
try:
mail = CustomMail.objects.get(shortname=fields['shortname'])
status['unchanged'] += 1
mail = CustomMail.objects.get(shortname=fields["shortname"])
status["unchanged"] += 1
except CustomMail.DoesNotExist:
mail = CustomMail.objects.create(**fields)
status['synced'] += 1
status["synced"] += 1
if verbosity:
log('SYNCED {:s}'.format(fields['shortname']))
assoc['mails'][obj['pk']] = mail
log("SYNCED {:s}".format(fields["shortname"]))
assoc["mails"][obj["pk"]] = mail
# Variables
if obj['model'] == 'custommail.custommailvariable':
fields['custommail'] = assoc['mails'].get(fields['custommail'])
fields['type'] = assoc['types'].get(fields['type'])
if obj["model"] == "custommail.custommailvariable":
fields["custommail"] = assoc["mails"].get(fields["custommail"])
fields["type"] = assoc["types"].get(fields["type"])
try:
Variable.objects.get(
custommail=fields['custommail'],
name=fields['name']
custommail=fields["custommail"], name=fields["name"]
)
except Variable.DoesNotExist:
Variable.objects.create(**fields)
if verbosity:
log(
'{synced:d} mails synchronized {unchanged:d} unchanged'
.format(**status)
)
log("{synced:d} mails synchronized {unchanged:d} unchanged".format(**status))
class Command(BaseCommand):
help = ("Va chercher les données mails de GestioCOF stocké au format json "
"dans /gestioncof/management/data/custommails.json. Le format des "
"données est celui donné par la commande :"
" `python manage.py dumpdata custommail --natural-foreign` "
"La bonne façon de mettre à jour ce fichier est donc de le "
"charger à l'aide de syncmails, le faire les modifications à "
"l'aide de l'interface administration et/ou du shell puis de le "
"remplacer par le nouveau résultat de la commande précédente.")
help = (
"Va chercher les données mails de GestioCOF stocké au format json "
"dans /gestioncof/management/data/custommails.json. Le format des "
"données est celui donné par la commande :"
" `python manage.py dumpdata custommail --natural-foreign` "
"La bonne façon de mettre à jour ce fichier est donc de le "
"charger à l'aide de syncmails, le faire les modifications à "
"l'aide de l'interface administration et/ou du shell puis de le "
"remplacer par le nouveau résultat de la commande précédente."
)
def handle(self, *args, **options):
load_from_file(log=self.stdout.write)

File diff suppressed because it is too large Load diff

View file

@ -6,14 +6,14 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('gestioncof', '0001_initial'),
]
dependencies = [("gestioncof", "0001_initial")]
operations = [
migrations.AlterField(
model_name='petitcoursdemande',
name='processed',
field=models.DateTimeField(null=True, verbose_name='Date de traitement', blank=True),
),
model_name="petitcoursdemande",
name="processed",
field=models.DateTimeField(
null=True, verbose_name="Date de traitement", blank=True
),
)
]

View file

@ -6,14 +6,14 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('gestioncof', '0002_enable_unprocessed_demandes'),
]
dependencies = [("gestioncof", "0002_enable_unprocessed_demandes")]
operations = [
migrations.AddField(
model_name='event',
name='image',
field=models.ImageField(upload_to=b'imgs/events/', null=True, verbose_name=b'Image', blank=True),
),
model_name="event",
name="image",
field=models.ImageField(
upload_to=b"imgs/events/", null=True, verbose_name=b"Image", blank=True
),
)
]

View file

@ -8,27 +8,28 @@ def create_mail(apps, schema_editor):
CustomMail = apps.get_model("gestioncof", "CustomMail")
db_alias = schema_editor.connection.alias
if CustomMail.objects.filter(shortname="bienvenue").count() == 0:
CustomMail.objects.using(db_alias).bulk_create([
CustomMail(
shortname="bienvenue",
title="Bienvenue au COF",
content="Mail de bienvenue au COF, envoyé automatiquement à " \
+ "l'inscription.\n\n" \
+ "Les balises {{ ... }} sont interprétées comme expliqué " \
CustomMail.objects.using(db_alias).bulk_create(
[
CustomMail(
shortname="bienvenue",
title="Bienvenue au COF",
content="Mail de bienvenue au COF, envoyé automatiquement à "
+ "l'inscription.\n\n"
+ "Les balises {{ ... }} sont interprétées comme expliqué "
+ "ci-dessous à l'envoi.",
comments="{{ nom }} \t fullname de la personne.\n"\
+ "{{ prenom }} \t prénom de la personne.")
])
comments="{{ nom }} \t fullname de la personne.\n"
+ "{{ prenom }} \t prénom de la personne.",
)
]
)
class Migration(migrations.Migration):
dependencies = [
('gestioncof', '0003_event_image'),
]
dependencies = [("gestioncof", "0003_event_image")]
operations = [
# Pas besoin de supprimer le mail lors de la migration dans l'autre
# sens.
migrations.RunPython(create_mail, migrations.RunPython.noop),
migrations.RunPython(create_mail, migrations.RunPython.noop)
]

View file

@ -6,62 +6,71 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('gestioncof', '0004_registration_mail'),
]
dependencies = [("gestioncof", "0004_registration_mail")]
operations = [
migrations.AlterModelOptions(
name='custommail',
options={'verbose_name': 'Mail personnalisable', 'verbose_name_plural': 'Mails personnalisables'},
name="custommail",
options={
"verbose_name": "Mail personnalisable",
"verbose_name_plural": "Mails personnalisables",
},
),
migrations.AlterModelOptions(
name='eventoptionchoice',
options={'verbose_name': 'Choix', 'verbose_name_plural': 'Choix'},
name="eventoptionchoice",
options={"verbose_name": "Choix", "verbose_name_plural": "Choix"},
),
migrations.AlterField(
model_name='cofprofile',
name='is_buro',
field=models.BooleanField(default=False, verbose_name='Membre du Bur\xf4'),
model_name="cofprofile",
name="is_buro",
field=models.BooleanField(default=False, verbose_name="Membre du Bur\xf4"),
),
migrations.AlterField(
model_name='cofprofile',
name='num',
field=models.IntegerField(default=0, verbose_name="Num\xe9ro d'adh\xe9rent", blank=True),
model_name="cofprofile",
name="num",
field=models.IntegerField(
default=0, verbose_name="Num\xe9ro d'adh\xe9rent", blank=True
),
),
migrations.AlterField(
model_name='cofprofile',
name='phone',
field=models.CharField(max_length=20, verbose_name='T\xe9l\xe9phone', blank=True),
model_name="cofprofile",
name="phone",
field=models.CharField(
max_length=20, verbose_name="T\xe9l\xe9phone", blank=True
),
),
migrations.AlterField(
model_name='event',
name='old',
field=models.BooleanField(default=False, verbose_name='Archiver (\xe9v\xe9nement fini)'),
model_name="event",
name="old",
field=models.BooleanField(
default=False, verbose_name="Archiver (\xe9v\xe9nement fini)"
),
),
migrations.AlterField(
model_name='event',
name='start_date',
field=models.DateField(null=True, verbose_name='Date de d\xe9but', blank=True),
model_name="event",
name="start_date",
field=models.DateField(
null=True, verbose_name="Date de d\xe9but", blank=True
),
),
migrations.AlterField(
model_name='eventcommentfield',
name='default',
field=models.TextField(verbose_name='Valeur par d\xe9faut', blank=True),
model_name="eventcommentfield",
name="default",
field=models.TextField(verbose_name="Valeur par d\xe9faut", blank=True),
),
migrations.AlterField(
model_name='eventregistration',
name='paid',
field=models.BooleanField(default=False, verbose_name='A pay\xe9'),
model_name="eventregistration",
name="paid",
field=models.BooleanField(default=False, verbose_name="A pay\xe9"),
),
migrations.AlterField(
model_name='survey',
name='details',
field=models.TextField(verbose_name='D\xe9tails', blank=True),
model_name="survey",
name="details",
field=models.TextField(verbose_name="D\xe9tails", blank=True),
),
migrations.AlterField(
model_name='surveyquestionanswer',
name='answer',
field=models.CharField(max_length=200, verbose_name='R\xe9ponse'),
model_name="surveyquestionanswer",
name="answer",
field=models.CharField(max_length=200, verbose_name="R\xe9ponse"),
),
]

View file

@ -1,51 +1,66 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('bda', '0004_mails-rappel'),
("bda", "0004_mails-rappel"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('gestioncof', '0005_encoding'),
("gestioncof", "0005_encoding"),
]
operations = [
migrations.CreateModel(
name='CalendarSubscription',
name="CalendarSubscription",
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False,
auto_created=True, primary_key=True)),
('token', models.UUIDField()),
('subscribe_to_events', models.BooleanField(default=True)),
('subscribe_to_my_shows', models.BooleanField(default=True)),
('other_shows', models.ManyToManyField(to='bda.Spectacle')),
('user', models.OneToOneField(to=settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)),
(
"id",
models.AutoField(
verbose_name="ID",
serialize=False,
auto_created=True,
primary_key=True,
),
),
("token", models.UUIDField()),
("subscribe_to_events", models.BooleanField(default=True)),
("subscribe_to_my_shows", models.BooleanField(default=True)),
("other_shows", models.ManyToManyField(to="bda.Spectacle")),
(
"user",
models.OneToOneField(
to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE
),
),
],
),
migrations.AlterModelOptions(
name='custommail',
options={'verbose_name': 'Mail personnalisable',
'verbose_name_plural': 'Mails personnalisables'},
name="custommail",
options={
"verbose_name": "Mail personnalisable",
"verbose_name_plural": "Mails personnalisables",
},
),
migrations.AlterModelOptions(
name='eventoptionchoice',
options={'verbose_name': 'Choix', 'verbose_name_plural': 'Choix'},
name="eventoptionchoice",
options={"verbose_name": "Choix", "verbose_name_plural": "Choix"},
),
migrations.AlterField(
model_name='event',
name='end_date',
field=models.DateTimeField(null=True, verbose_name=b'Date de fin',
blank=True),
),
migrations.AlterField(
model_name='event',
name='start_date',
model_name="event",
name="end_date",
field=models.DateTimeField(
null=True, verbose_name=b'Date de d\xc3\xa9but', blank=True),
null=True, verbose_name=b"Date de fin", blank=True
),
),
migrations.AlterField(
model_name="event",
name="start_date",
field=models.DateTimeField(
null=True, verbose_name=b"Date de d\xc3\xa9but", blank=True
),
),
]

View file

@ -1,47 +1,44 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('gestioncof', '0006_add_calendar'),
]
dependencies = [("gestioncof", "0006_add_calendar")]
operations = [
migrations.AlterField(
model_name='club',
name='name',
field=models.CharField(unique=True, max_length=200,
verbose_name='Nom')
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)
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),
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),
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),
model_name="event",
name="start_date",
field=models.DateTimeField(
null=True, verbose_name="Date de d\xe9but", blank=True
),
),
]

View file

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.db import migrations, models
def forwards(apps, schema_editor):
@ -11,243 +11,266 @@ def forwards(apps, schema_editor):
class Migration(migrations.Migration):
dependencies = [
('gestioncof', '0007_alter_club'),
]
dependencies = [("gestioncof", "0007_alter_club")]
operations = [
migrations.AlterField(
model_name='clipper',
name='fullname',
field=models.CharField(verbose_name='Nom complet', max_length=200),
model_name="clipper",
name="fullname",
field=models.CharField(verbose_name="Nom complet", max_length=200),
),
migrations.AlterField(
model_name='clipper',
name='username',
field=models.CharField(verbose_name='Identifiant', max_length=20),
model_name="clipper",
name="username",
field=models.CharField(verbose_name="Identifiant", max_length=20),
),
migrations.AlterField(
model_name='cofprofile',
name='comments',
model_name="cofprofile",
name="comments",
field=models.TextField(
verbose_name="Commentaires visibles par l'utilisateur",
blank=True),
verbose_name="Commentaires visibles par l'utilisateur", blank=True
),
),
migrations.AlterField(
model_name='cofprofile',
name='is_cof',
field=models.BooleanField(verbose_name='Membre du COF',
default=False),
model_name="cofprofile",
name="is_cof",
field=models.BooleanField(verbose_name="Membre du COF", default=False),
),
migrations.AlterField(
model_name='cofprofile',
name='login_clipper',
field=models.CharField(verbose_name='Login clipper', max_length=8,
blank=True),
model_name="cofprofile",
name="login_clipper",
field=models.CharField(
verbose_name="Login clipper", max_length=8, blank=True
),
),
migrations.AlterField(
model_name='cofprofile',
name='mailing_bda',
field=models.BooleanField(verbose_name='Recevoir les mails BdA',
default=False),
),
migrations.AlterField(
model_name='cofprofile',
name='mailing_bda_revente',
model_name="cofprofile",
name="mailing_bda",
field=models.BooleanField(
verbose_name='Recevoir les mails de revente de places BdA',
default=False),
verbose_name="Recevoir les mails BdA", default=False
),
),
migrations.AlterField(
model_name='cofprofile',
name='mailing_cof',
field=models.BooleanField(verbose_name='Recevoir les mails COF',
default=False),
model_name="cofprofile",
name="mailing_bda_revente",
field=models.BooleanField(
verbose_name="Recevoir les mails de revente de places BdA",
default=False,
),
),
migrations.AlterField(
model_name='cofprofile',
name='occupation',
field=models.CharField(verbose_name='Occupation',
choices=[('exterieur', 'Extérieur'),
('1A', '1A'),
('2A', '2A'),
('3A', '3A'),
('4A', '4A'),
('archicube', 'Archicube'),
('doctorant', 'Doctorant'),
('CST', 'CST')],
max_length=9, default='1A'),
model_name="cofprofile",
name="mailing_cof",
field=models.BooleanField(
verbose_name="Recevoir les mails COF", default=False
),
),
migrations.AlterField(
model_name='cofprofile',
name='petits_cours_accept',
field=models.BooleanField(verbose_name='Recevoir des petits cours',
default=False),
model_name="cofprofile",
name="occupation",
field=models.CharField(
verbose_name="Occupation",
choices=[
("exterieur", "Extérieur"),
("1A", "1A"),
("2A", "2A"),
("3A", "3A"),
("4A", "4A"),
("archicube", "Archicube"),
("doctorant", "Doctorant"),
("CST", "CST"),
],
max_length=9,
default="1A",
),
),
migrations.AlterField(
model_name='cofprofile',
name='petits_cours_remarques',
model_name="cofprofile",
name="petits_cours_accept",
field=models.BooleanField(
verbose_name="Recevoir des petits cours", default=False
),
),
migrations.AlterField(
model_name="cofprofile",
name="petits_cours_remarques",
field=models.TextField(
blank=True,
verbose_name='Remarques et précisions pour les petits cours',
default=''),
verbose_name="Remarques et précisions pour les petits cours",
default="",
),
),
migrations.AlterField(
model_name='cofprofile',
name='type_cotiz',
model_name="cofprofile",
name="type_cotiz",
field=models.CharField(
verbose_name='Type de cotisation',
choices=[('etudiant', 'Normalien étudiant'),
('normalien', 'Normalien élève'),
('exterieur', 'Extérieur')],
max_length=9, default='normalien'),
verbose_name="Type de cotisation",
choices=[
("etudiant", "Normalien étudiant"),
("normalien", "Normalien élève"),
("exterieur", "Extérieur"),
],
max_length=9,
default="normalien",
),
),
migrations.AlterField(
model_name='custommail',
name='comments',
model_name="custommail",
name="comments",
field=models.TextField(
verbose_name='Informations contextuelles sur le mail',
blank=True),
verbose_name="Informations contextuelles sur le mail", blank=True
),
),
migrations.AlterField(
model_name='custommail',
name='content',
field=models.TextField(verbose_name='Contenu'),
model_name="custommail",
name="content",
field=models.TextField(verbose_name="Contenu"),
),
migrations.AlterField(
model_name='custommail',
name='title',
field=models.CharField(verbose_name='Titre', max_length=200),
model_name="custommail",
name="title",
field=models.CharField(verbose_name="Titre", max_length=200),
),
migrations.AlterField(
model_name='event',
name='description',
field=models.TextField(verbose_name='Description', blank=True),
model_name="event",
name="description",
field=models.TextField(verbose_name="Description", blank=True),
),
migrations.AlterField(
model_name='event',
name='end_date',
field=models.DateTimeField(null=True, verbose_name='Date de fin',
blank=True),
model_name="event",
name="end_date",
field=models.DateTimeField(
null=True, verbose_name="Date de fin", blank=True
),
),
migrations.AlterField(
model_name='event',
name='image',
field=models.ImageField(upload_to='imgs/events/', null=True,
verbose_name='Image', blank=True),
model_name="event",
name="image",
field=models.ImageField(
upload_to="imgs/events/", null=True, verbose_name="Image", blank=True
),
),
migrations.AlterField(
model_name='event',
name='location',
field=models.CharField(verbose_name='Lieu', max_length=200),
model_name="event",
name="location",
field=models.CharField(verbose_name="Lieu", max_length=200),
),
migrations.AlterField(
model_name='event',
name='registration_open',
field=models.BooleanField(verbose_name='Inscriptions ouvertes',
default=True),
model_name="event",
name="registration_open",
field=models.BooleanField(
verbose_name="Inscriptions ouvertes", default=True
),
),
migrations.AlterField(
model_name='event',
name='title',
field=models.CharField(verbose_name='Titre', max_length=200),
model_name="event",
name="title",
field=models.CharField(verbose_name="Titre", max_length=200),
),
migrations.AlterField(
model_name='eventcommentfield',
name='fieldtype',
field=models.CharField(verbose_name='Type',
choices=[('text', 'Texte long'),
('char', 'Texte court')],
max_length=10, default='text'),
),
migrations.AlterField(
model_name='eventcommentfield',
name='name',
field=models.CharField(verbose_name='Champ', max_length=200),
),
migrations.AlterField(
model_name='eventcommentvalue',
name='content',
field=models.TextField(null=True, verbose_name='Contenu',
blank=True),
),
migrations.AlterField(
model_name='eventoption',
name='multi_choices',
field=models.BooleanField(verbose_name='Choix multiples',
default=False),
),
migrations.AlterField(
model_name='eventoption',
name='name',
field=models.CharField(verbose_name='Option', max_length=200),
),
migrations.AlterField(
model_name='eventoptionchoice',
name='value',
field=models.CharField(verbose_name='Valeur', max_length=200),
),
migrations.AlterField(
model_name='petitcoursability',
name='niveau',
model_name="eventcommentfield",
name="fieldtype",
field=models.CharField(
choices=[('college', 'Collège'), ('lycee', 'Lycée'),
('prepa1styear', 'Prépa 1ère année / L1'),
('prepa2ndyear', 'Prépa 2ème année / L2'),
('licence3', 'Licence 3'),
('other', 'Autre (préciser dans les commentaires)')],
max_length=12, verbose_name='Niveau'),
verbose_name="Type",
choices=[("text", "Texte long"), ("char", "Texte court")],
max_length=10,
default="text",
),
),
migrations.AlterField(
model_name='petitcoursattribution',
name='rank',
model_name="eventcommentfield",
name="name",
field=models.CharField(verbose_name="Champ", max_length=200),
),
migrations.AlterField(
model_name="eventcommentvalue",
name="content",
field=models.TextField(null=True, verbose_name="Contenu", blank=True),
),
migrations.AlterField(
model_name="eventoption",
name="multi_choices",
field=models.BooleanField(verbose_name="Choix multiples", default=False),
),
migrations.AlterField(
model_name="eventoption",
name="name",
field=models.CharField(verbose_name="Option", max_length=200),
),
migrations.AlterField(
model_name="eventoptionchoice",
name="value",
field=models.CharField(verbose_name="Valeur", max_length=200),
),
migrations.AlterField(
model_name="petitcoursability",
name="niveau",
field=models.CharField(
choices=[
("college", "Collège"),
("lycee", "Lycée"),
("prepa1styear", "Prépa 1ère année / L1"),
("prepa2ndyear", "Prépa 2ème année / L2"),
("licence3", "Licence 3"),
("other", "Autre (préciser dans les commentaires)"),
],
max_length=12,
verbose_name="Niveau",
),
),
migrations.AlterField(
model_name="petitcoursattribution",
name="rank",
field=models.IntegerField(verbose_name="Rang dans l'email"),
),
migrations.AlterField(
model_name='petitcoursattributioncounter',
name='count',
field=models.IntegerField(verbose_name="Nombre d'envois",
default=0),
model_name="petitcoursattributioncounter",
name="count",
field=models.IntegerField(verbose_name="Nombre d'envois", default=0),
),
migrations.AlterField(
model_name='petitcoursdemande',
name='niveau',
model_name="petitcoursdemande",
name="niveau",
field=models.CharField(
verbose_name='Niveau',
choices=[('college', 'Collège'), ('lycee', 'Lycée'),
('prepa1styear', 'Prépa 1ère année / L1'),
('prepa2ndyear', 'Prépa 2ème année / L2'),
('licence3', 'Licence 3'),
('other', 'Autre (préciser dans les commentaires)')],
max_length=12, default=''),
verbose_name="Niveau",
choices=[
("college", "Collège"),
("lycee", "Lycée"),
("prepa1styear", "Prépa 1ère année / L1"),
("prepa2ndyear", "Prépa 2ème année / L2"),
("licence3", "Licence 3"),
("other", "Autre (préciser dans les commentaires)"),
],
max_length=12,
default="",
),
),
migrations.AlterField(
model_name='survey',
name='old',
field=models.BooleanField(verbose_name='Archiver (sondage fini)',
default=False),
model_name="survey",
name="old",
field=models.BooleanField(
verbose_name="Archiver (sondage fini)", default=False
),
),
migrations.AlterField(
model_name='survey',
name='survey_open',
field=models.BooleanField(verbose_name='Sondage ouvert',
default=True),
model_name="survey",
name="survey_open",
field=models.BooleanField(verbose_name="Sondage ouvert", default=True),
),
migrations.AlterField(
model_name='survey',
name='title',
field=models.CharField(verbose_name='Titre', max_length=200),
model_name="survey",
name="title",
field=models.CharField(verbose_name="Titre", max_length=200),
),
migrations.AlterField(
model_name='surveyquestion',
name='multi_answers',
field=models.BooleanField(verbose_name='Choix multiples',
default=False),
model_name="surveyquestion",
name="multi_answers",
field=models.BooleanField(verbose_name="Choix multiples", default=False),
),
migrations.AlterField(
model_name='surveyquestion',
name='question',
field=models.CharField(verbose_name='Question', max_length=200),
model_name="surveyquestion",
name="question",
field=models.CharField(verbose_name="Question", max_length=200),
),
migrations.RunPython(forwards, migrations.RunPython.noop),
]

View file

@ -6,12 +6,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('gestioncof', '0008_py3'),
]
dependencies = [("gestioncof", "0008_py3")]
operations = [
migrations.DeleteModel(
name='Clipper',
),
]
operations = [migrations.DeleteModel(name="Clipper")]

View file

@ -5,12 +5,6 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('gestioncof', '0009_delete_clipper'),
]
dependencies = [("gestioncof", "0009_delete_clipper")]
operations = [
migrations.DeleteModel(
name='CustomMail',
),
]
operations = [migrations.DeleteModel(name="CustomMail")]

View file

@ -6,14 +6,14 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('gestioncof', '0010_delete_custommail'),
]
dependencies = [("gestioncof", "0010_delete_custommail")]
operations = [
migrations.AlterField(
model_name='cofprofile',
name='login_clipper',
field=models.CharField(verbose_name='Login clipper', blank=True, max_length=32),
),
model_name="cofprofile",
name="login_clipper",
field=models.CharField(
verbose_name="Login clipper", blank=True, max_length=32
),
)
]

View file

@ -6,13 +6,6 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('gestioncof', '0010_delete_custommail'),
]
dependencies = [("gestioncof", "0010_delete_custommail")]
operations = [
migrations.RemoveField(
model_name='cofprofile',
name='num',
),
]
operations = [migrations.RemoveField(model_name="cofprofile", name="num")]

View file

@ -7,9 +7,8 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('gestioncof', '0011_remove_cofprofile_num'),
('gestioncof', '0011_longer_clippers'),
("gestioncof", "0011_remove_cofprofile_num"),
("gestioncof", "0011_longer_clippers"),
]
operations = [
]
operations = []

View file

@ -6,42 +6,42 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('gestioncof', '0012_merge'),
]
dependencies = [("gestioncof", "0012_merge")]
operations = [
migrations.AlterField(
model_name='cofprofile',
name='occupation',
model_name="cofprofile",
name="occupation",
field=models.CharField(
verbose_name='Occupation',
verbose_name="Occupation",
max_length=9,
default='1A',
default="1A",
choices=[
('exterieur', 'Extérieur'),
('1A', '1A'),
('2A', '2A'),
('3A', '3A'),
('4A', '4A'),
('archicube', 'Archicube'),
('doctorant', 'Doctorant'),
('CST', 'CST'),
('PEI', 'PEI')
]),
("exterieur", "Extérieur"),
("1A", "1A"),
("2A", "2A"),
("3A", "3A"),
("4A", "4A"),
("archicube", "Archicube"),
("doctorant", "Doctorant"),
("CST", "CST"),
("PEI", "PEI"),
],
),
),
migrations.AlterField(
model_name='cofprofile',
name='type_cotiz',
model_name="cofprofile",
name="type_cotiz",
field=models.CharField(
verbose_name='Type de cotisation',
verbose_name="Type de cotisation",
max_length=9,
default='normalien',
default="normalien",
choices=[
('etudiant', 'Normalien étudiant'),
('normalien', 'Normalien élève'),
('exterieur', 'Extérieur'),
('gratis', 'Gratuit')
]),
("etudiant", "Normalien étudiant"),
("normalien", "Normalien élève"),
("exterieur", "Extérieur"),
("gratis", "Gratuit"),
],
),
),
]

View file

@ -7,14 +7,14 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('gestioncof', '0013_pei'),
]
dependencies = [("gestioncof", "0013_pei")]
operations = [
migrations.AddField(
model_name='cofprofile',
name='mailing_unernestaparis',
field=models.BooleanField(default=False, verbose_name='Recevoir les mails unErnestAParis'),
),
model_name="cofprofile",
name="mailing_unernestaparis",
field=models.BooleanField(
default=False, verbose_name="Recevoir les mails unErnestAParis"
),
)
]

View file

@ -1,17 +1,13 @@
from django.db import models
from django.dispatch import receiver
from django.contrib.auth.models import User
from django.db import models
from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _
from django.db.models.signals import post_save, post_delete
from gestioncof.petits_cours_models import choices_length
from bda.models import Spectacle
from gestioncof.petits_cours_models import choices_length
TYPE_COMMENT_FIELD = (
('text', _("Texte long")),
('char', _("Texte court")),
)
TYPE_COMMENT_FIELD = (("text", _("Texte long")), ("char", _("Texte court")))
class CofProfile(models.Model):
@ -49,40 +45,39 @@ class CofProfile(models.Model):
(COTIZ_GRATIS, _("Gratuit")),
)
user = models.OneToOneField(
User, on_delete=models.CASCADE,
related_name="profile",
)
login_clipper = models.CharField(
"Login clipper", max_length=32, blank=True
)
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="profile")
login_clipper = models.CharField("Login clipper", max_length=32, blank=True)
is_cof = models.BooleanField("Membre du COF", default=False)
phone = models.CharField("Téléphone", max_length=20, blank=True)
occupation = models.CharField(_("Occupation"),
default="1A",
choices=OCCUPATION_CHOICES,
max_length=choices_length(
OCCUPATION_CHOICES))
departement = models.CharField(_("Département"), max_length=50,
blank=True)
type_cotiz = models.CharField(_("Type de cotisation"),
default="normalien",
choices=TYPE_COTIZ_CHOICES,
max_length=choices_length(
TYPE_COTIZ_CHOICES))
occupation = models.CharField(
_("Occupation"),
default="1A",
choices=OCCUPATION_CHOICES,
max_length=choices_length(OCCUPATION_CHOICES),
)
departement = models.CharField(_("Département"), max_length=50, blank=True)
type_cotiz = models.CharField(
_("Type de cotisation"),
default="normalien",
choices=TYPE_COTIZ_CHOICES,
max_length=choices_length(TYPE_COTIZ_CHOICES),
)
mailing_cof = models.BooleanField("Recevoir les mails COF", default=False)
mailing_bda = models.BooleanField("Recevoir les mails BdA", default=False)
mailing_unernestaparis = models.BooleanField("Recevoir les mails unErnestAParis", default=False)
mailing_unernestaparis = models.BooleanField(
"Recevoir les mails unErnestAParis", default=False
)
mailing_bda_revente = models.BooleanField(
"Recevoir les mails de revente de places BdA", default=False)
comments = models.TextField(
"Commentaires visibles par l'utilisateur", blank=True)
"Recevoir les mails de revente de places BdA", default=False
)
comments = models.TextField("Commentaires visibles par l'utilisateur", blank=True)
is_buro = models.BooleanField("Membre du Burô", default=False)
petits_cours_accept = models.BooleanField(
"Recevoir des petits cours", default=False)
"Recevoir des petits cours", default=False
)
petits_cours_remarques = models.TextField(
_("Remarques et précisions pour les petits cours"),
blank=True, default="")
_("Remarques et précisions pour les petits cours"), blank=True, default=""
)
class Meta:
verbose_name = "Profil COF"
@ -106,8 +101,7 @@ def post_delete_user(sender, instance, *args, **kwargs):
class Club(models.Model):
name = models.CharField("Nom", max_length=200, unique=True)
description = models.TextField("Description", blank=True)
respos = models.ManyToManyField(User, related_name="clubs_geres",
blank=True)
respos = models.ManyToManyField(User, related_name="clubs_geres", blank=True)
membres = models.ManyToManyField(User, related_name="clubs", blank=True)
def __str__(self):
@ -120,10 +114,8 @@ class Event(models.Model):
start_date = models.DateTimeField("Date de début", blank=True, null=True)
end_date = models.DateTimeField("Date de fin", blank=True, null=True)
description = models.TextField("Description", blank=True)
image = models.ImageField("Image", blank=True, null=True,
upload_to="imgs/events/")
registration_open = models.BooleanField("Inscriptions ouvertes",
default=True)
image = models.ImageField("Image", blank=True, null=True, upload_to="imgs/events/")
registration_open = models.BooleanField("Inscriptions ouvertes", default=True)
old = models.BooleanField("Archiver (événement fini)", default=False)
class Meta:
@ -135,12 +127,12 @@ class Event(models.Model):
class EventCommentField(models.Model):
event = models.ForeignKey(
Event, on_delete=models.CASCADE,
related_name="commentfields",
Event, on_delete=models.CASCADE, related_name="commentfields"
)
name = models.CharField("Champ", max_length=200)
fieldtype = models.CharField("Type", max_length=10,
choices=TYPE_COMMENT_FIELD, default="text")
fieldtype = models.CharField(
"Type", max_length=10, choices=TYPE_COMMENT_FIELD, default="text"
)
default = models.TextField("Valeur par défaut", blank=True)
class Meta:
@ -152,12 +144,10 @@ class EventCommentField(models.Model):
class EventCommentValue(models.Model):
commentfield = models.ForeignKey(
EventCommentField, on_delete=models.CASCADE,
related_name="values",
EventCommentField, on_delete=models.CASCADE, related_name="values"
)
registration = models.ForeignKey(
"EventRegistration", on_delete=models.CASCADE,
related_name="comments",
"EventRegistration", on_delete=models.CASCADE, related_name="comments"
)
content = models.TextField("Contenu", blank=True, null=True)
@ -166,10 +156,7 @@ class EventCommentValue(models.Model):
class EventOption(models.Model):
event = models.ForeignKey(
Event, on_delete=models.CASCADE,
related_name="options",
)
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="options")
name = models.CharField("Option", max_length=200)
multi_choices = models.BooleanField("Choix multiples", default=False)
@ -182,8 +169,7 @@ class EventOption(models.Model):
class EventOptionChoice(models.Model):
event_option = models.ForeignKey(
EventOption, on_delete=models.CASCADE,
related_name="choices",
EventOption, on_delete=models.CASCADE, related_name="choices"
)
value = models.CharField("Valeur", max_length=200)
@ -199,8 +185,9 @@ class EventRegistration(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
event = models.ForeignKey(Event, on_delete=models.CASCADE)
options = models.ManyToManyField(EventOptionChoice)
filledcomments = models.ManyToManyField(EventCommentField,
through=EventCommentValue)
filledcomments = models.ManyToManyField(
EventCommentField, through=EventCommentValue
)
paid = models.BooleanField("A payé", default=False)
class Meta:
@ -226,8 +213,7 @@ class Survey(models.Model):
class SurveyQuestion(models.Model):
survey = models.ForeignKey(
Survey, on_delete=models.CASCADE,
related_name="questions",
Survey, on_delete=models.CASCADE, related_name="questions"
)
question = models.CharField("Question", max_length=200)
multi_answers = models.BooleanField("Choix multiples", default=False)
@ -241,8 +227,7 @@ class SurveyQuestion(models.Model):
class SurveyQuestionAnswer(models.Model):
survey_question = models.ForeignKey(
SurveyQuestion, on_delete=models.CASCADE,
related_name="answers",
SurveyQuestion, on_delete=models.CASCADE, related_name="answers"
)
answer = models.CharField("Réponse", max_length=200)
@ -256,8 +241,7 @@ class SurveyQuestionAnswer(models.Model):
class SurveyAnswer(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
survey = models.ForeignKey(Survey, on_delete=models.CASCADE)
answers = models.ManyToManyField(SurveyQuestionAnswer,
related_name="selected_by")
answers = models.ManyToManyField(SurveyQuestionAnswer, related_name="selected_by")
class Meta:
verbose_name = "Réponses"
@ -265,8 +249,9 @@ class SurveyAnswer(models.Model):
def __str__(self):
return "Réponse de %s sondage %s" % (
self.user.get_full_name(),
self.survey.title)
self.user.get_full_name(),
self.survey.title,
)
class CalendarSubscription(models.Model):

View file

@ -1,11 +1,10 @@
from captcha.fields import ReCaptchaField
from django import forms
from django.forms import ModelForm
from django.forms.models import inlineformset_factory, BaseInlineFormSet
from django.contrib.auth.models import User
from django.forms import ModelForm
from django.forms.models import BaseInlineFormSet, inlineformset_factory
from gestioncof.petits_cours_models import PetitCoursDemande, PetitCoursAbility
from gestioncof.petits_cours_models import PetitCoursAbility, PetitCoursDemande
class BaseMatieresFormSet(BaseInlineFormSet):
@ -20,33 +19,44 @@ class BaseMatieresFormSet(BaseInlineFormSet):
form = self.forms[i]
if not form.cleaned_data:
continue
matiere = form.cleaned_data['matiere']
niveau = form.cleaned_data['niveau']
delete = form.cleaned_data['DELETE']
matiere = form.cleaned_data["matiere"]
niveau = form.cleaned_data["niveau"]
delete = form.cleaned_data["DELETE"]
if not delete and (matiere, niveau) in matieres:
raise forms.ValidationError(
"Vous ne pouvez pas vous inscrire deux fois pour la "
"même matiere avec le même niveau.")
"même matiere avec le même niveau."
)
matieres.append((matiere, niveau))
class DemandeForm(ModelForm):
captcha = ReCaptchaField(attrs={'theme': 'clean', 'lang': 'fr'})
captcha = ReCaptchaField(attrs={"theme": "clean", "lang": "fr"})
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['matieres'].help_text = ''
self.fields["matieres"].help_text = ""
class Meta:
model = PetitCoursDemande
fields = ('name', 'email', 'phone', 'quand', 'freq', 'lieu',
'matieres', 'agrege_requis', 'niveau', 'remarques')
widgets = {'matieres': forms.CheckboxSelectMultiple}
fields = (
"name",
"email",
"phone",
"quand",
"freq",
"lieu",
"matieres",
"agrege_requis",
"niveau",
"remarques",
)
widgets = {"matieres": forms.CheckboxSelectMultiple}
MatieresFormSet = inlineformset_factory(
User,
PetitCoursAbility,
fields=("matiere", "niveau", "agrege"),
formset=BaseMatieresFormSet
formset=BaseMatieresFormSet,
)

View file

@ -1,28 +1,30 @@
from functools import reduce
from django.contrib.auth.models import User
from django.db import models
from django.db.models import Min
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
def choices_length(choices):
return reduce(lambda m, choice: max(m, len(choice[0])), choices, 0)
LEVELS_CHOICES = (
('college', _("Collège")),
('lycee', _("Lycée")),
('prepa1styear', _("Prépa 1ère année / L1")),
('prepa2ndyear', _("Prépa 2ème année / L2")),
('licence3', _("Licence 3")),
('other', _("Autre (préciser dans les commentaires)")),
("college", _("Collège")),
("lycee", _("Lycée")),
("prepa1styear", _("Prépa 1ère année / L1")),
("prepa2ndyear", _("Prépa 2ème année / L2")),
("licence3", _("Licence 3")),
("other", _("Autre (préciser dans les commentaires)")),
)
class PetitCoursSubject(models.Model):
name = models.CharField(_("Matière"), max_length=30)
users = models.ManyToManyField(User, related_name="petits_cours_matieres",
through="PetitCoursAbility")
users = models.ManyToManyField(
User, related_name="petits_cours_matieres", through="PetitCoursAbility"
)
class Meta:
verbose_name = "Matière de petits cours"
@ -35,12 +37,11 @@ class PetitCoursSubject(models.Model):
class PetitCoursAbility(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
matiere = models.ForeignKey(
PetitCoursSubject, on_delete=models.CASCADE,
verbose_name=_("Matière"),
PetitCoursSubject, on_delete=models.CASCADE, verbose_name=_("Matière")
)
niveau = models.CharField(
_("Niveau"), choices=LEVELS_CHOICES, max_length=choices_length(LEVELS_CHOICES)
)
niveau = models.CharField(_("Niveau"),
choices=LEVELS_CHOICES,
max_length=choices_length(LEVELS_CHOICES))
agrege = models.BooleanField(_("Agrégé"), default=False)
class Meta:
@ -56,41 +57,50 @@ class PetitCoursAbility(models.Model):
class PetitCoursDemande(models.Model):
name = models.CharField(_("Nom/prénom"), max_length=200)
email = models.CharField(_("Adresse email"), max_length=300)
phone = models.CharField(_("Téléphone (facultatif)"),
max_length=20, blank=True)
phone = models.CharField(_("Téléphone (facultatif)"), max_length=20, blank=True)
quand = models.CharField(
_("Quand ?"),
help_text=_("Indiquez ici la période désirée pour les petits"
" cours (vacances scolaires, semaine, week-end)."),
max_length=300, blank=True)
help_text=_(
"Indiquez ici la période désirée pour les petits"
" cours (vacances scolaires, semaine, week-end)."
),
max_length=300,
blank=True,
)
freq = models.CharField(
_("Fréquence"),
help_text=_("Indiquez ici la fréquence envisagée "
"(hebdomadaire, 2 fois par semaine, ...)"),
max_length=300, blank=True)
help_text=_(
"Indiquez ici la fréquence envisagée "
"(hebdomadaire, 2 fois par semaine, ...)"
),
max_length=300,
blank=True,
)
lieu = models.CharField(
_("Lieu (si préférence)"),
help_text=_("Si vous avez avez une préférence sur le lieu."),
max_length=300, blank=True)
max_length=300,
blank=True,
)
matieres = models.ManyToManyField(
PetitCoursSubject, verbose_name=_("Matières"),
related_name="demandes")
PetitCoursSubject, verbose_name=_("Matières"), related_name="demandes"
)
agrege_requis = models.BooleanField(_("Agrégé requis"), default=False)
niveau = models.CharField(_("Niveau"),
default="",
choices=LEVELS_CHOICES,
max_length=choices_length(LEVELS_CHOICES))
niveau = models.CharField(
_("Niveau"),
default="",
choices=LEVELS_CHOICES,
max_length=choices_length(LEVELS_CHOICES),
)
remarques = models.TextField(_("Remarques et précisions"), blank=True)
traitee = models.BooleanField(_("Traitée"), default=False)
traitee_par = models.ForeignKey(
User, on_delete=models.CASCADE,
blank=True, null=True,
User, on_delete=models.CASCADE, blank=True, null=True
)
processed = models.DateTimeField(_("Date de traitement"),
blank=True, null=True)
processed = models.DateTimeField(_("Date de traitement"), blank=True, null=True)
created = models.DateTimeField(_("Date de création"), auto_now_add=True)
def get_candidates(self, redo=False):
@ -105,18 +115,15 @@ class PetitCoursDemande(models.Model):
matiere=matiere,
niveau=self.niveau,
user__profile__is_cof=True,
user__profile__petits_cours_accept=True
user__profile__petits_cours_accept=True,
)
if self.agrege_requis:
candidates = candidates.filter(agrege=True)
if redo:
attrs = self.petitcoursattribution_set.filter(matiere=matiere)
already_proposed = [
attr.user
for attr in attrs
]
already_proposed = [attr.user for attr in attrs]
candidates = candidates.exclude(user__in=already_proposed)
candidates = candidates.order_by('?').select_related().all()
candidates = candidates.order_by("?").select_related().all()
yield (matiere, candidates)
class Meta:
@ -124,25 +131,20 @@ class PetitCoursDemande(models.Model):
verbose_name_plural = "Demandes de petits cours"
def __str__(self):
return "Demande {:d} du {:s}".format(
self.id, self.created.strftime("%d %b %Y")
)
return "Demande {:d} du {:s}".format(self.id, self.created.strftime("%d %b %Y"))
class PetitCoursAttribution(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
demande = models.ForeignKey(
PetitCoursDemande, on_delete=models.CASCADE,
verbose_name=_("Demande"),
PetitCoursDemande, on_delete=models.CASCADE, verbose_name=_("Demande")
)
matiere = models.ForeignKey(
PetitCoursSubject, on_delete=models.CASCADE,
verbose_name=_("Matière"),
PetitCoursSubject, on_delete=models.CASCADE, verbose_name=_("Matière")
)
date = models.DateTimeField(_("Date d'attribution"), auto_now_add=True)
rank = models.IntegerField("Rang dans l'email")
selected = models.BooleanField(_("Sélectionné par le demandeur"),
default=False)
selected = models.BooleanField(_("Sélectionné par le demandeur"), default=False)
class Meta:
verbose_name = "Attribution de petits cours"
@ -157,8 +159,7 @@ class PetitCoursAttribution(models.Model):
class PetitCoursAttributionCounter(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
matiere = models.ForeignKey(
PetitCoursSubject, on_delete=models.CASCADE,
verbose_name=_("Matiere"),
PetitCoursSubject, on_delete=models.CASCADE, verbose_name=_("Matiere")
)
count = models.IntegerField("Nombre d'envois", default=0)
@ -169,15 +170,12 @@ class PetitCoursAttributionCounter(models.Model):
n'existe pas encore, il est initialisé avec le minimum des valeurs des
compteurs de tout le monde.
"""
counter, created = cls.objects.get_or_create(
user=user,
matiere=matiere,
)
counter, created = cls.objects.get_or_create(user=user, matiere=matiere)
if created:
mincount = (
cls.objects.filter(matiere=matiere).exclude(user=user)
.aggregate(Min('count'))
['count__min']
cls.objects.filter(matiere=matiere)
.exclude(user=user)
.aggregate(Min("count"))["count__min"]
)
counter.count = mincount or 0
counter.save()

View file

@ -1,31 +1,31 @@
import json
from custommail.shortcuts import render_custom_mail
from django.shortcuts import render, get_object_or_404, redirect
from django.core import mail
from django.contrib.auth.models import User
from django.views.generic import ListView, DetailView
from django.views.decorators.csrf import csrf_exempt
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.core import mail
from django.db import transaction
from django.shortcuts import get_object_or_404, redirect, render
from django.utils import timezone
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import DetailView, ListView
from gestioncof.models import CofProfile
from gestioncof.petits_cours_models import (
PetitCoursDemande, PetitCoursAttribution, PetitCoursAttributionCounter,
PetitCoursAbility
)
from gestioncof.petits_cours_forms import DemandeForm, MatieresFormSet
from gestioncof.decorators import buro_required
from gestioncof.models import CofProfile
from gestioncof.petits_cours_forms import DemandeForm, MatieresFormSet
from gestioncof.petits_cours_models import (
PetitCoursAbility,
PetitCoursAttribution,
PetitCoursAttributionCounter,
PetitCoursDemande,
)
class DemandeListView(ListView):
queryset = (
PetitCoursDemande.objects
.prefetch_related('matieres')
.order_by('traitee', '-id')
queryset = PetitCoursDemande.objects.prefetch_related("matieres").order_by(
"traitee", "-id"
)
template_name = "petits_cours_demandes_list.html"
paginate_by = 20
@ -33,10 +33,8 @@ class DemandeListView(ListView):
class DemandeDetailView(DetailView):
model = PetitCoursDemande
queryset = (
PetitCoursDemande.objects
.prefetch_related('petitcoursattribution_set',
'matieres')
queryset = PetitCoursDemande.objects.prefetch_related(
"petitcoursattribution_set", "matieres"
)
template_name = "gestioncof/details_demande_petit_cours.html"
context_object_name = "demande"
@ -44,7 +42,7 @@ class DemandeDetailView(DetailView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
obj = self.object
context['attributions'] = obj.petitcoursattribution_set.all()
context["attributions"] = obj.petitcoursattribution_set.all()
return context
@ -64,13 +62,15 @@ def traitement(request, demande_id, redo=False):
tuples = []
for candidate in candidates:
user = candidate.user
tuples.append((
candidate,
PetitCoursAttributionCounter.get_uptodate(user, matiere)
))
tuples.append(
(
candidate,
PetitCoursAttributionCounter.get_uptodate(user, matiere),
)
)
tuples = sorted(tuples, key=lambda c: c[1].count)
candidates, _ = zip(*tuples)
candidates = candidates[0:min(3, len(candidates))]
candidates = candidates[0 : min(3, len(candidates))]
attribdata[matiere.id] = []
proposals[matiere] = []
for candidate in candidates:
@ -83,8 +83,9 @@ def traitement(request, demande_id, redo=False):
proposed_for[user].append(matiere)
else:
unsatisfied.append(matiere)
return _finalize_traitement(request, demande, proposals,
proposed_for, unsatisfied, attribdata, redo)
return _finalize_traitement(
request, demande, proposals, proposed_for, unsatisfied, attribdata, redo
)
@buro_required
@ -92,43 +93,56 @@ def retraitement(request, demande_id):
return traitement(request, demande_id, redo=True)
def _finalize_traitement(request, demande, proposals, proposed_for,
unsatisfied, attribdata, redo=False, errors=None):
def _finalize_traitement(
request,
demande,
proposals,
proposed_for,
unsatisfied,
attribdata,
redo=False,
errors=None,
):
proposals = proposals.items()
proposed_for = proposed_for.items()
attribdata = list(attribdata.items())
proposed_mails = _generate_eleve_email(demande, proposed_for)
mainmail = render_custom_mail("petits-cours-mail-demandeur", {
"proposals": proposals,
"unsatisfied": unsatisfied,
"extra":
'<textarea name="extra" '
mainmail = render_custom_mail(
"petits-cours-mail-demandeur",
{
"proposals": proposals,
"unsatisfied": unsatisfied,
"extra": '<textarea name="extra" '
'style="width:99%; height: 90px;">'
'</textarea>'
})
"</textarea>",
},
)
if errors is not None:
for error in errors:
messages.error(request, error)
return render(request, "gestioncof/traitement_demande_petit_cours.html",
{"demande": demande,
"unsatisfied": unsatisfied,
"proposals": proposals,
"proposed_for": proposed_for,
"proposed_mails": proposed_mails,
"mainmail": mainmail,
"attribdata": json.dumps(attribdata),
"redo": redo,
})
return render(
request,
"gestioncof/traitement_demande_petit_cours.html",
{
"demande": demande,
"unsatisfied": unsatisfied,
"proposals": proposals,
"proposed_for": proposed_for,
"proposed_mails": proposed_mails,
"mainmail": mainmail,
"attribdata": json.dumps(attribdata),
"redo": redo,
},
)
def _generate_eleve_email(demande, proposed_for):
return [
(
user,
render_custom_mail('petit-cours-mail-eleve', {
"demande": demande,
"matieres": matieres
})
render_custom_mail(
"petit-cours-mail-eleve", {"demande": demande, "matieres": matieres}
),
)
for user, matieres in proposed_for
]
@ -143,25 +157,30 @@ def _traitement_other_preparing(request, demande):
errors = []
for matiere, candidates in demande.get_candidates(redo):
if candidates:
candidates = dict([(candidate.user.id, candidate.user)
for candidate in candidates])
candidates = dict(
[(candidate.user.id, candidate.user) for candidate in candidates]
)
attribdata[matiere.id] = []
proposals[matiere] = []
for choice_id in range(min(3, len(candidates))):
choice = int(
request.POST["proposal-{:d}-{:d}"
.format(matiere.id, choice_id)]
request.POST["proposal-{:d}-{:d}".format(matiere.id, choice_id)]
)
if choice == -1:
continue
if choice not in candidates:
errors.append("Choix invalide pour la proposition {:d}"
"en {!s}".format(choice_id + 1, matiere))
errors.append(
"Choix invalide pour la proposition {:d}"
"en {!s}".format(choice_id + 1, matiere)
)
continue
user = candidates[choice]
if user in proposals[matiere]:
errors.append("La proposition {:d} en {!s} est un doublon"
.format(choice_id + 1, matiere))
errors.append(
"La proposition {:d} en {!s} est un doublon".format(
choice_id + 1, matiere
)
)
continue
proposals[matiere].append(user)
attribdata[matiere.id].append(user.id)
@ -172,15 +191,24 @@ def _traitement_other_preparing(request, demande):
if not proposals[matiere]:
errors.append("Aucune proposition pour {!s}".format(matiere))
elif len(proposals[matiere]) < 3:
errors.append("Seulement {:d} proposition{:s} pour {!s}"
.format(
len(proposals[matiere]),
"s" if len(proposals[matiere]) > 1 else "",
matiere))
errors.append(
"Seulement {:d} proposition{:s} pour {!s}".format(
len(proposals[matiere]),
"s" if len(proposals[matiere]) > 1 else "",
matiere,
)
)
else:
unsatisfied.append(matiere)
return _finalize_traitement(request, demande, proposals, proposed_for,
unsatisfied, attribdata, errors=errors)
return _finalize_traitement(
request,
demande,
proposals,
proposed_for,
unsatisfied,
attribdata,
errors=errors,
)
def _traitement_other(request, demande, redo):
@ -198,10 +226,12 @@ def _traitement_other(request, demande, redo):
tuples = []
for candidate in candidates:
user = candidate.user
tuples.append((
candidate,
PetitCoursAttributionCounter.get_uptodate(user, matiere)
))
tuples.append(
(
candidate,
PetitCoursAttributionCounter.get_uptodate(user, matiere),
)
)
tuples = sorted(tuples, key=lambda c: c[1].count)
candidates, _ = zip(*tuples)
attribdata[matiere.id] = []
@ -218,13 +248,16 @@ def _traitement_other(request, demande, redo):
unsatisfied.append(matiere)
proposals = proposals.items()
proposed_for = proposed_for.items()
return render(request,
"gestioncof/traitement_demande_petit_cours_autre_niveau.html",
{"demande": demande,
"unsatisfied": unsatisfied,
"proposals": proposals,
"proposed_for": proposed_for,
})
return render(
request,
"gestioncof/traitement_demande_petit_cours_autre_niveau.html",
{
"demande": demande,
"unsatisfied": unsatisfied,
"proposals": proposals,
"proposed_for": proposed_for,
},
)
def _traitement_post(request, demande):
@ -252,24 +285,32 @@ def _traitement_post(request, demande):
proposed_mails = _generate_eleve_email(demande, proposed_for)
mainmail_object, mainmail_body = render_custom_mail(
"petits-cours-mail-demandeur",
{
"proposals": proposals_list,
"unsatisfied": unsatisfied,
"extra": extra
}
{"proposals": proposals_list, "unsatisfied": unsatisfied, "extra": extra},
)
frommail = settings.MAIL_DATA['petits_cours']['FROM']
bccaddress = settings.MAIL_DATA['petits_cours']['BCC']
replyto = settings.MAIL_DATA['petits_cours']['REPLYTO']
frommail = settings.MAIL_DATA["petits_cours"]["FROM"]
bccaddress = settings.MAIL_DATA["petits_cours"]["BCC"]
replyto = settings.MAIL_DATA["petits_cours"]["REPLYTO"]
mails_to_send = []
for (user, (mail_object, body)) in proposed_mails:
msg = mail.EmailMessage(mail_object, body, frommail, [user.email],
[bccaddress], headers={'Reply-To': replyto})
msg = mail.EmailMessage(
mail_object,
body,
frommail,
[user.email],
[bccaddress],
headers={"Reply-To": replyto},
)
mails_to_send.append(msg)
mails_to_send.append(mail.EmailMessage(mainmail_object, mainmail_body,
frommail, [demande.email],
[bccaddress],
headers={'Reply-To': replyto}))
mails_to_send.append(
mail.EmailMessage(
mainmail_object,
mainmail_body,
frommail,
[demande.email],
[bccaddress],
headers={"Reply-To": replyto},
)
)
connection = mail.get_connection(fail_silently=False)
connection.send_messages(mails_to_send)
with transaction.atomic():
@ -280,18 +321,19 @@ def _traitement_post(request, demande):
)
counter.count += 1
counter.save()
attrib = PetitCoursAttribution(user=user, matiere=matiere,
demande=demande, rank=rank + 1)
attrib = PetitCoursAttribution(
user=user, matiere=matiere, demande=demande, rank=rank + 1
)
attrib.save()
demande.traitee = True
demande.traitee_par = request.user
demande.processed = timezone.now()
demande.save()
return render(request,
"gestioncof/traitement_demande_petit_cours_success.html",
{"demande": demande,
"redo": redo,
})
return render(
request,
"gestioncof/traitement_demande_petit_cours_success.html",
{"demande": demande, "redo": redo},
)
@login_required
@ -308,22 +350,25 @@ def inscription(request):
profile.petits_cours_remarques = request.POST["remarques"]
profile.save()
with transaction.atomic():
abilities = (
PetitCoursAbility.objects.filter(user=request.user).all()
)
abilities = PetitCoursAbility.objects.filter(user=request.user).all()
for ability in abilities:
PetitCoursAttributionCounter.get_uptodate(
ability.user,
ability.matiere
ability.user, ability.matiere
)
success = True
formset = MatieresFormSet(instance=request.user)
else:
formset = MatieresFormSet(instance=request.user)
return render(request, "inscription-petit-cours.html",
{"formset": formset, "success": success,
"receive_proposals": profile.petits_cours_accept,
"remarques": profile.petits_cours_remarques})
return render(
request,
"inscription-petit-cours.html",
{
"formset": formset,
"success": success,
"receive_proposals": profile.petits_cours_accept,
"remarques": profile.petits_cours_remarques,
},
)
@csrf_exempt
@ -336,8 +381,9 @@ def demande(request):
success = True
else:
form = DemandeForm()
return render(request, "demande-petit-cours.html", {"form": form,
"success": success})
return render(
request, "demande-petit-cours.html", {"form": form, "success": success}
)
@csrf_exempt
@ -350,5 +396,6 @@ def demande_raw(request):
success = True
else:
form = DemandeForm()
return render(request, "demande-petit-cours-raw.html",
{"form": form, "success": success})
return render(
request, "demande-petit-cours-raw.html", {"form": form, "success": success}
)

View file

@ -1,13 +1,11 @@
from django.conf import settings
from django.contrib.sites.models import Site
from django_cas_ng.backends import CASBackend
from gestioncof.models import CofProfile
class COFCASBackend(CASBackend):
def clean_username(self, username):
# Le CAS de l'ENS accepte les logins avec des espaces au début
# et à la fin, ainsi quavec une casse variable. On normalise pour
@ -24,9 +22,6 @@ class COFCASBackend(CASBackend):
def context_processor(request):
'''Append extra data to the context of the given request'''
data = {
"user": request.user,
"site": Site.objects.get_current(),
}
"""Append extra data to the context of the given request"""
data = {"user": request.user, "site": Site.objects.get_current()}
return data

View file

@ -2,22 +2,21 @@ from django.contrib import messages
from django.contrib.auth.signals import user_logged_in
from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _
from django_cas_ng.signals import cas_user_authenticated
@receiver(user_logged_in)
def messages_on_out_login(request, user, **kwargs):
if user.backend.startswith('django.contrib.auth'):
msg = _('Connexion à GestioCOF réussie. Bienvenue {}.').format(
user.get_short_name(),
if user.backend.startswith("django.contrib.auth"):
msg = _("Connexion à GestioCOF réussie. Bienvenue {}.").format(
user.get_short_name()
)
messages.success(request, msg)
@receiver(cas_user_authenticated)
def mesagges_on_cas_login(request, user, **kwargs):
msg = _('Connexion à GestioCOF par CAS réussie. Bienvenue {}.').format(
user.get_short_name(),
msg = _("Connexion à GestioCOF par CAS réussie. Bienvenue {}.").format(
user.get_short_name()
)
messages.success(request, msg)

View file

@ -1,8 +1,8 @@
import re
from django import template
from django.utils.safestring import mark_safe
import re
register = template.Library()
@ -12,6 +12,7 @@ def key(d, key_name):
value = d[key_name]
except KeyError:
from django.conf import settings
value = settings.TEMPLATE_STRING_IF_INVALID
return value
@ -19,16 +20,15 @@ def key(d, key_name):
def highlight_text(text, q):
q2 = "|".join(re.escape(word) for word in q.split())
pattern = re.compile(r"(?P<filter>%s)" % q2, re.IGNORECASE)
return mark_safe(re.sub(pattern,
r"<span class='highlight'>\g<filter></span>",
text))
return mark_safe(
re.sub(pattern, r"<span class='highlight'>\g<filter></span>", text)
)
@register.filter
def highlight_user(user, q):
if user.first_name and user.last_name:
text = "%s %s (<tt>%s</tt>)" % (user.first_name, user.last_name,
user.username)
text = "%s %s (<tt>%s</tt>)" % (user.first_name, user.last_name, user.username)
else:
text = user.username
return highlight_text(text, q)

View file

@ -12,17 +12,17 @@ from gestioncof.models import CofProfile, User
class SimpleTest(TestCase):
def test_delete_user(self):
u = User(username='foo', first_name='foo', last_name='bar')
u = User(username="foo", first_name="foo", last_name="bar")
# to each user there's a cofprofile associated
u.save()
self.assertTrue(CofProfile.objects.filter(user__username='foo').exists())
self.assertTrue(CofProfile.objects.filter(user__username="foo").exists())
# there's no point in having a cofprofile without a user associated.
u.delete()
self.assertFalse(CofProfile.objects.filter(user__username='foo').exists())
self.assertFalse(CofProfile.objects.filter(user__username="foo").exists())
# there's no point in having a user without a cofprofile associated.
u.save()
CofProfile.objects.get(user__username='foo').delete()
self.assertFalse(User.objects.filter(username='foo').exists())
CofProfile.objects.get(user__username="foo").delete()
self.assertFalse(User.objects.filter(username="foo").exists())

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
from shared.tests.testcases import ViewTestCaseMixin as BaseViewTestCaseMixin
from .utils import create_user, create_member, create_staff
from .utils import create_member, create_staff, create_user
class ViewTestCaseMixin(BaseViewTestCaseMixin):
@ -18,7 +18,7 @@ class ViewTestCaseMixin(BaseViewTestCaseMixin):
def get_users_base(self):
return {
'user': create_user('user'),
'member': create_member('member'),
'staff': create_staff('staff'),
"user": create_user("user"),
"member": create_member("member"),
"staff": create_staff("staff"),
}

View file

@ -7,28 +7,35 @@ def _create_user(username, is_cof=False, is_staff=False, attrs=None):
if attrs is None:
attrs = {}
password = attrs.pop('password', username)
password = attrs.pop("password", username)
user_keys = [
'first_name', 'last_name', 'email', 'is_staff', 'is_superuser',
]
user_keys = ["first_name", "last_name", "email", "is_staff", "is_superuser"]
user_attrs = {k: v for k, v in attrs.items() if k in user_keys}
profile_keys = [
'is_cof', 'login_clipper', 'phone', 'occupation', 'departement',
'type_cotiz', 'mailing_cof', 'mailing_bda', 'mailing_bda_revente',
'comments', 'is_buro', 'petit_cours_accept',
'petit_cours_remarques',
"is_cof",
"login_clipper",
"phone",
"occupation",
"departement",
"type_cotiz",
"mailing_cof",
"mailing_bda",
"mailing_bda_revente",
"comments",
"is_buro",
"petit_cours_accept",
"petit_cours_remarques",
]
profile_attrs = {k: v for k, v in attrs.items() if k in profile_keys}
if is_cof:
profile_attrs['is_cof'] = True
profile_attrs["is_cof"] = True
if is_staff:
# At the moment, admin is accessible by COF staff.
user_attrs['is_staff'] = True
profile_attrs['is_buro'] = True
user_attrs["is_staff"] = True
profile_attrs["is_buro"] = True
user = User(username=username, **user_attrs)
user.set_password(password)
@ -56,6 +63,6 @@ def create_staff(username, attrs=None):
def create_root(username, attrs=None):
if attrs is None:
attrs = {}
attrs.setdefault('is_staff', True)
attrs.setdefault('is_superuser', True)
attrs.setdefault("is_staff", True)
attrs.setdefault("is_superuser", True)
return _create_user(username, attrs=attrs)

View file

@ -1,67 +1,87 @@
from django.conf.urls import url
from gestioncof.petits_cours_views import DemandeListView, DemandeDetailView
from gestioncof import views, petits_cours_views
from gestioncof import petits_cours_views, views
from gestioncof.decorators import buro_required
from gestioncof.petits_cours_views import DemandeDetailView, DemandeListView
export_patterns = [
url(r'^members$', views.export_members,
name='cof.membres_export'),
url(r'^mega/avecremarques$', views.export_mega_remarksonly,
name='cof.mega_export_remarks'),
url(r'^mega/participants$', views.export_mega_participants,
name='cof.mega_export_participants'),
url(r'^mega/orgas$', views.export_mega_orgas,
name='cof.mega_export_orgas'),
url(r"^members$", views.export_members, name="cof.membres_export"),
url(
r"^mega/avecremarques$",
views.export_mega_remarksonly,
name="cof.mega_export_remarks",
),
url(
r"^mega/participants$",
views.export_mega_participants,
name="cof.mega_export_participants",
),
url(r"^mega/orgas$", views.export_mega_orgas, name="cof.mega_export_orgas"),
# url(r'^mega/(?P<type>.+)$', views.export_mega_bytype),
url(r'^mega$', views.export_mega,
name='cof.mega_export'),
url(r"^mega$", views.export_mega, name="cof.mega_export"),
]
petitcours_patterns = [
url(r'^inscription$', petits_cours_views.inscription,
name='petits-cours-inscription'),
url(r'^demande$', petits_cours_views.demande,
name='petits-cours-demande'),
url(r'^demande-raw$', petits_cours_views.demande_raw,
name='petits-cours-demande-raw'),
url(r'^demandes$',
url(
r"^inscription$",
petits_cours_views.inscription,
name="petits-cours-inscription",
),
url(r"^demande$", petits_cours_views.demande, name="petits-cours-demande"),
url(
r"^demande-raw$",
petits_cours_views.demande_raw,
name="petits-cours-demande-raw",
),
url(
r"^demandes$",
buro_required(DemandeListView.as_view()),
name='petits-cours-demandes-list'),
url(r'^demandes/(?P<pk>\d+)$',
name="petits-cours-demandes-list",
),
url(
r"^demandes/(?P<pk>\d+)$",
buro_required(DemandeDetailView.as_view()),
name='petits-cours-demande-details'),
url(r'^demandes/(?P<demande_id>\d+)/traitement$',
name="petits-cours-demande-details",
),
url(
r"^demandes/(?P<demande_id>\d+)/traitement$",
petits_cours_views.traitement,
name='petits-cours-demande-traitement'),
url(r'^demandes/(?P<demande_id>\d+)/retraitement$',
name="petits-cours-demande-traitement",
),
url(
r"^demandes/(?P<demande_id>\d+)/retraitement$",
petits_cours_views.retraitement,
name='petits-cours-demande-retraitement'),
name="petits-cours-demande-retraitement",
),
]
surveys_patterns = [
url(r'^(?P<survey_id>\d+)/status$', views.survey_status,
name='survey.details.status'),
url(r'^(?P<survey_id>\d+)$', views.survey,
name='survey.details'),
url(
r"^(?P<survey_id>\d+)/status$",
views.survey_status,
name="survey.details.status",
),
url(r"^(?P<survey_id>\d+)$", views.survey, name="survey.details"),
]
events_patterns = [
url(r'^(?P<event_id>\d+)$', views.event,
name='event.details'),
url(r'^(?P<event_id>\d+)/status$', views.event_status,
name='event.details.status'),
url(r"^(?P<event_id>\d+)$", views.event, name="event.details"),
url(r"^(?P<event_id>\d+)/status$", views.event_status, name="event.details.status"),
]
calendar_patterns = [
url(r'^subscription$', views.calendar,
name='calendar'),
url(r'^(?P<token>[a-z0-9-]+)/calendar.ics$', views.calendar_ics,
name='calendar.ics'),
url(r"^subscription$", views.calendar, name="calendar"),
url(
r"^(?P<token>[a-z0-9-]+)/calendar.ics$", views.calendar_ics, name="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'),
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

@ -1,59 +1,74 @@
import unicodecsv
import uuid
from datetime import timedelta
from icalendar import Calendar, Event as Vevent
from custommail.shortcuts import send_custom_mail
from django.shortcuts import redirect, get_object_or_404, render
from django.http import Http404, HttpResponse, HttpResponseForbidden
import unicodecsv
from custommail.shortcuts import send_custom_mail
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.contrib.auth.views import (
login as django_login_view, logout as django_logout_view,
login as django_login_view,
logout as django_logout_view,
redirect_to_login,
)
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.core.urlresolvers import reverse_lazy
from django.views.generic import FormView
from django.http import Http404, HttpResponse, HttpResponseForbidden
from django.shortcuts import get_object_or_404, redirect, render
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from django.contrib import messages
from django.views.generic import FormView
from django_cas_ng.views import logout as cas_logout_view
from icalendar import Calendar, Event as Vevent
from utils.views.autocomplete import Select2QuerySetView
from gestioncof.models import Survey, SurveyAnswer, SurveyQuestion, \
SurveyQuestionAnswer
from gestioncof.models import Event, EventRegistration, EventOption, \
EventOptionChoice
from gestioncof.models import EventCommentField, EventCommentValue, \
CalendarSubscription
from gestioncof.models import CofProfile, Club
from bda.models import Spectacle, Tirage
from gestioncof.decorators import buro_required, cof_required
from gestioncof.forms import (
UserForm, ProfileForm,
EventStatusFilterForm, SurveyForm, SurveyStatusFilterForm,
RegistrationUserForm, RegistrationProfileForm, EventForm, CalendarForm,
EventFormset, RegistrationPassUserForm, ClubsForm, GestioncofConfigForm
CalendarForm,
ClubsForm,
EventForm,
EventFormset,
EventStatusFilterForm,
GestioncofConfigForm,
ProfileForm,
RegistrationPassUserForm,
RegistrationProfileForm,
RegistrationUserForm,
SurveyForm,
SurveyStatusFilterForm,
UserForm,
)
from bda.models import Tirage, Spectacle
from gestioncof.models import (
CalendarSubscription,
Club,
CofProfile,
Event,
EventCommentField,
EventCommentValue,
EventOption,
EventOptionChoice,
EventRegistration,
Survey,
SurveyAnswer,
SurveyQuestion,
SurveyQuestionAnswer,
)
from utils.views.autocomplete import Select2QuerySetView
@login_required
def home(request):
data = {"surveys": Survey.objects.filter(old=False).all(),
"events": Event.objects.filter(old=False).all(),
"open_surveys":
Survey.objects.filter(survey_open=True, old=False).all(),
"open_events":
Event.objects.filter(registration_open=True, old=False).all(),
"active_tirages": Tirage.objects.filter(active=True).all(),
"open_tirages":
Tirage.objects.filter(active=True,
ouverture__lte=timezone.now()).all(),
"now": timezone.now()}
data = {
"surveys": Survey.objects.filter(old=False).all(),
"events": Event.objects.filter(old=False).all(),
"open_surveys": Survey.objects.filter(survey_open=True, old=False).all(),
"open_events": Event.objects.filter(registration_open=True, old=False).all(),
"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)
@ -61,8 +76,8 @@ def login(request):
if request.user.is_authenticated:
return redirect("home")
context = {}
if request.method == "GET" and 'next' in request.GET:
context['next'] = request.GET['next']
if request.method == "GET" and "next" in request.GET:
context["next"] = request.GET["next"]
return render(request, "login_switch.html", context)
@ -73,34 +88,33 @@ def login_ext(request):
if not user.has_usable_password() or user.password in ("", "!"):
profile, created = CofProfile.objects.get_or_create(user=user)
if profile.login_clipper:
return render(request, "error.html",
{"error_type": "use_clipper_login"})
return render(
request, "error.html", {"error_type": "use_clipper_login"}
)
else:
return render(request, "error.html",
{"error_type": "no_password"})
return render(request, "error.html", {"error_type": "no_password"})
except User.DoesNotExist:
pass
context = {}
if request.method == "GET" and 'next' in request.GET:
context['next'] = request.GET['next']
if request.method == "POST" and 'next' in request.POST:
context['next'] = request.POST['next']
return django_login_view(request, template_name='login.html',
extra_context=context)
if request.method == "GET" and "next" in request.GET:
context["next"] = request.GET["next"]
if request.method == "POST" and "next" in request.POST:
context["next"] = request.POST["next"]
return django_login_view(request, template_name="login.html", extra_context=context)
@login_required
def logout(request, next_page=None):
if next_page is None:
next_page = request.GET.get('next', None)
next_page = request.GET.get("next", None)
profile = getattr(request.user, 'profile', None)
profile = getattr(request.user, "profile", None)
if profile and profile.login_clipper:
msg = _('Déconnexion de GestioCOF et CAS réussie. À bientôt {}.')
msg = _("Déconnexion de GestioCOF et CAS réussie. À bientôt {}.")
logout_view = cas_logout_view
else:
msg = _('Déconnexion de GestioCOF réussie. À bientôt {}.')
msg = _("Déconnexion de GestioCOF réussie. À bientôt {}.")
logout_view = django_logout_view
messages.success(request, msg.format(request.user.get_short_name()))
@ -110,8 +124,7 @@ def logout(request, next_page=None):
@login_required
def survey(request, survey_id):
survey = get_object_or_404(
Survey.objects.prefetch_related('questions', 'questions__answers'),
id=survey_id,
Survey.objects.prefetch_related("questions", "questions__answers"), id=survey_id
)
if not survey.survey_open or survey.old:
raise Http404
@ -119,10 +132,11 @@ def survey(request, survey_id):
deleted = False
if request.method == "POST":
form = SurveyForm(request.POST, survey=survey)
if request.POST.get('delete'):
if request.POST.get("delete"):
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:
@ -134,9 +148,9 @@ def survey(request, survey_id):
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)
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:
@ -146,50 +160,48 @@ def survey(request, survey_id):
continue
answer_id = int(answer_id)
answer = SurveyQuestionAnswer.objects.get(
id=answer_id,
survey_question=question)
id=answer_id, survey_question=question
)
all_answers.append(answer)
try:
current_answer = SurveyAnswer.objects.get(
user=request.user, survey=survey)
user=request.user, survey=survey
)
except SurveyAnswer.DoesNotExist:
current_answer = SurveyAnswer(user=request.user,
survey=survey)
current_answer = SurveyAnswer(user=request.user, survey=survey)
current_answer.save()
current_answer.answers = all_answers
current_answer.save()
success = True
else:
try:
current_answer = SurveyAnswer.objects.get(user=request.user,
survey=survey)
form = SurveyForm(survey=survey,
current_answers=current_answer.answers)
current_answer = SurveyAnswer.objects.get(user=request.user, survey=survey)
form = SurveyForm(survey=survey, current_answers=current_answer.answers)
except SurveyAnswer.DoesNotExist:
current_answer = None
form = SurveyForm(survey=survey)
# Messages
if success:
if deleted:
messages.success(request,
"Votre réponse a bien été supprimée")
messages.success(request, "Votre réponse a bien été supprimée")
else:
messages.success(request,
"Votre réponse a bien été enregistrée ! Vous "
"pouvez cependant la modifier jusqu'à la fin "
"du sondage.")
return render(request, "gestioncof/survey.html", {
"survey": survey,
"form": form,
"current_answer": current_answer
})
messages.success(
request,
"Votre réponse a bien été enregistrée ! Vous "
"pouvez cependant la modifier jusqu'à la fin "
"du sondage.",
)
return render(
request,
"gestioncof/survey.html",
{"survey": survey, "form": form, "current_answer": current_answer},
)
def get_event_form_choices(event, form):
all_choices = []
for option_id, choices_ids in form.choices():
option = get_object_or_404(EventOption, id=option_id,
event=event)
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:
@ -198,22 +210,19 @@ def get_event_form_choices(event, form):
if not choice_id:
continue
choice_id = int(choice_id)
choice = EventOptionChoice.objects.get(
id=choice_id,
event_option=option)
choice = EventOptionChoice.objects.get(id=choice_id, event_option=option)
all_choices.append(choice)
return all_choices
def update_event_form_comments(event, form, registration):
for commentfield_id, value in form.comments():
field = get_object_or_404(EventCommentField, id=commentfield_id,
event=event)
field = get_object_or_404(EventCommentField, id=commentfield_id, event=event)
if value == field.default:
continue
(storage, _) = EventCommentValue.objects.get_or_create(
commentfield=field,
registration=registration)
commentfield=field, registration=registration
)
storage.content = value
storage.save()
@ -228,27 +237,29 @@ def event(request, event_id):
form = EventForm(request.POST, event=event)
if form.is_valid():
all_choices = get_event_form_choices(event, form)
(current_registration, _) = \
EventRegistration.objects.get_or_create(user=request.user,
event=event)
(current_registration, _) = EventRegistration.objects.get_or_create(
user=request.user, event=event
)
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)
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)
# Messages
if success:
messages.success(request, "Votre inscription a bien été enregistrée ! "
"Vous pouvez cependant la modifier jusqu'à "
"la fin des inscriptions.")
return render(request, "gestioncof/event.html",
{"event": event, "form": form})
messages.success(
request,
"Votre inscription a bien été enregistrée ! "
"Vous pouvez cependant la modifier jusqu'à "
"la fin des inscriptions.",
)
return render(request, "gestioncof/event.html", {"event": event, "form": form})
def clean_post_for_status(initial):
@ -272,19 +283,21 @@ def event_status(request, event_id):
if value == "yes":
registrations_query = registrations_query.filter(paid=True)
elif value == "no":
registrations_query = registrations_query.filter(
paid=False)
registrations_query = registrations_query.filter(paid=False)
continue
choice = get_object_or_404(EventOptionChoice, id=choice_id,
event_option__id=option_id)
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)
options__id__exact=choice.id
)
elif value == "no":
registrations_query = registrations_query.exclude(
options__id__exact=choice.id)
options__id__exact=choice.id
)
user_choices = registrations_query.prefetch_related("user").all()
options = EventOption.objects.filter(event=event).all()
choices_count = {}
@ -294,10 +307,17 @@ def event_status(request, event_id):
for user_choice in user_choices:
for choice in user_choice.options.all():
choices_count[choice.id] += 1
return render(request, "event_status.html",
{"event": event, "user_choices": user_choices,
"options": options, "choices_count": choices_count,
"form": form})
return render(
request,
"event_status.html",
{
"event": event,
"user_choices": user_choices,
"options": options,
"choices_count": choices_count,
"form": form,
},
)
@buro_required
@ -308,16 +328,15 @@ def survey_status(request, survey_id):
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)
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)
answers_query = answers_query.filter(answers__id__exact=answer.id)
elif value == "no":
answers_query = answers_query.exclude(
answers__id__exact=answer.id)
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 = {}
@ -327,10 +346,17 @@ def survey_status(request, survey_id):
for user_answer in user_answers:
for answer in user_answer.answers.all():
answers_count[answer.id] += 1
return render(request, "survey_status.html",
{"survey": survey, "user_answers": user_answers,
"questions": questions, "answers_count": answers_count,
"form": form})
return render(
request,
"survey_status.html",
{
"survey": survey,
"user_answers": user_answers,
"questions": questions,
"answers_count": answers_count,
"form": form,
},
)
@cof_required
@ -343,22 +369,18 @@ def profile(request):
if user_form.is_valid() and profile_form.is_valid():
user_form.save()
profile_form.save()
messages.success(
request,
_("Votre profil a été mis à jour avec succès !")
)
messages.success(request, _("Votre profil a été mis à jour avec succès !"))
context = {"user_form": user_form, "profile_form": profile_form}
return render(request, "gestioncof/profile.html", context)
def registration_set_ro_fields(user_form, profile_form):
user_form.fields['username'].widget.attrs['readonly'] = True
profile_form.fields['login_clipper'].widget.attrs['readonly'] = True
user_form.fields["username"].widget.attrs["readonly"] = True
profile_form.fields["login_clipper"].widget.attrs["readonly"] = True
@buro_required
def registration_form2(request, login_clipper=None, username=None,
fullname=None):
def registration_form2(request, login_clipper=None, username=None, fullname=None):
events = Event.objects.filter(old=False).all()
member = None
if login_clipper:
@ -369,20 +391,24 @@ def registration_form2(request, login_clipper=None, username=None,
except User.DoesNotExist:
# new user, but prefill
# user
user_form = RegistrationUserForm(initial={
'username': login_clipper,
'email': "%s@clipper.ens.fr" % login_clipper})
user_form = RegistrationUserForm(
initial={
"username": login_clipper,
"email": "%s@clipper.ens.fr" % login_clipper,
}
)
if fullname:
bits = fullname.split(" ")
user_form.fields['first_name'].initial = bits[0]
user_form.fields["first_name"].initial = bits[0]
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})
profile_form = RegistrationProfileForm(
initial={"login_clipper": login_clipper}
)
registration_set_ro_fields(user_form, profile_form)
# events & clubs
event_formset = EventFormset(events=events, prefix='events')
event_formset = EventFormset(events=events, prefix="events")
clubs_form = ClubsForm()
if username:
member = get_object_or_404(User, username=username)
@ -396,26 +422,33 @@ def registration_form2(request, login_clipper=None, username=None,
for event in events:
try:
current_registrations.append(
EventRegistration.objects.get(user=member, event=event))
EventRegistration.objects.get(user=member, event=event)
)
except EventRegistration.DoesNotExist:
current_registrations.append(None)
event_formset = EventFormset(
events=events, prefix='events',
current_registrations=current_registrations)
events=events, prefix="events", current_registrations=current_registrations
)
# Clubs
clubs_form = ClubsForm(initial={'clubs': member.clubs.all()})
clubs_form = ClubsForm(initial={"clubs": member.clubs.all()})
elif not login_clipper:
# new user
user_form = RegistrationPassUserForm()
profile_form = RegistrationProfileForm()
event_formset = EventFormset(events=events, prefix='events')
event_formset = EventFormset(events=events, prefix="events")
clubs_form = ClubsForm()
return render(request, "gestioncof/registration_form.html",
{"member": member, "login_clipper": login_clipper,
"user_form": user_form,
"profile_form": profile_form,
"event_formset": event_formset,
"clubs_form": clubs_form})
return render(
request,
"gestioncof/registration_form.html",
{
"member": member,
"login_clipper": login_clipper,
"user_form": user_form,
"profile_form": profile_form,
"event_formset": event_formset,
"clubs_form": clubs_form,
},
)
@buro_required
@ -429,15 +462,14 @@ def registration(request):
# Remplissage des formulaires
# -----
if 'password1' in request_dict or 'password2' in request_dict:
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')
event_formset = EventFormset(events=events, data=request_dict, prefix="events")
if "user_exists" in request_dict and request_dict["user_exists"]:
username = request_dict["username"]
try:
@ -459,38 +491,44 @@ def registration(request):
profile, _ = CofProfile.objects.get_or_create(user=member)
was_cof = profile.is_cof
# Maintenant on remplit le formulaire de profil
profile_form = RegistrationProfileForm(request_dict,
instance=profile)
if (profile_form.is_valid() and event_formset.is_valid()
and clubs_form.is_valid()):
profile_form = RegistrationProfileForm(request_dict, instance=profile)
if (
profile_form.is_valid()
and event_formset.is_valid()
and clubs_form.is_valid()
):
# Enregistrement du profil
profile = profile_form.save()
if profile.is_cof and not was_cof:
send_custom_mail(
"welcome", "cof@ens.fr", [member.email],
context={'member': member},
"welcome",
"cof@ens.fr",
[member.email],
context={"member": member},
)
# Enregistrement des inscriptions aux événements
for form in event_formset:
if 'status' not in form.cleaned_data:
form.cleaned_data['status'] = 'no'
if form.cleaned_data['status'] == 'no':
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 = 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)
update_event_form_comments(form.event, form,
current_registration)
(
current_registration,
created_reg,
) = EventRegistration.objects.get_or_create(
user=member, event=form.event
)
update_event_form_comments(form.event, form, current_registration)
current_registration.options = all_choices
current_registration.paid = \
(form.cleaned_data['status'] == 'paid')
current_registration.paid = form.cleaned_data["status"] == "paid"
current_registration.save()
# if form.event.title == "Mega 15" and created_reg:
# field = EventCommentField.objects.get(
@ -508,7 +546,7 @@ def registration(request):
# send_custom_mail(...)
# Enregistrement des inscriptions aux clubs
member.clubs.clear()
for club in clubs_form.cleaned_data['clubs']:
for club in clubs_form.cleaned_data["clubs"]:
club.membres.add(member)
club.save()
@ -516,20 +554,29 @@ def registration(request):
# Success
# ---
msg = ("L'inscription de {:s} (<tt>{:s}</tt>) a été "
"enregistrée avec succès."
.format(member.get_full_name(), member.email))
msg = (
"L'inscription de {:s} (<tt>{:s}</tt>) a été "
"enregistrée avec succès.".format(
member.get_full_name(), member.email
)
)
if profile.is_cof:
msg += "\nIl est désormais membre du COF n°{:d} !".format(
member.profile.id)
messages.success(request, msg, extra_tags='safe')
return render(request, "gestioncof/registration_post.html",
{"user_form": user_form,
"profile_form": profile_form,
"member": member,
"login_clipper": login_clipper,
"event_formset": event_formset,
"clubs_form": clubs_form})
member.profile.id
)
messages.success(request, msg, extra_tags="safe")
return render(
request,
"gestioncof/registration_post.html",
{
"user_form": user_form,
"profile_form": profile_form,
"member": member,
"login_clipper": login_clipper,
"event_formset": event_formset,
"clubs_form": clubs_form,
},
)
else:
return render(request, "registration.html")
@ -545,13 +592,14 @@ def membres_club(request, name):
# 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>')
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})
return render(
request,
"membres_clubs.html",
{"club": club, "members_no_respo": members_no_respo},
)
@buro_required
@ -564,31 +612,41 @@ def change_respo(request, club_name, user_id):
club.respos.add(user)
else:
raise Http404
return redirect('membres-club', name=club_name)
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()}
data = {"owned_clubs": clubs.all()}
else:
data = {'owned_clubs': request.user.clubs_geres.all(),
'other_clubs': clubs.exclude(respos=request.user)}
return render(request, 'liste_clubs.html', data)
data = {
"owned_clubs": request.user.clubs_geres.all(),
"other_clubs": clubs.exclude(respos=request.user),
}
return render(request, "liste_clubs.html", data)
@buro_required
def export_members(request):
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename=membres_cof.csv'
response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = "attachment; filename=membres_cof.csv"
writer = unicodecsv.writer(response)
for profile in CofProfile.objects.filter(is_cof=True).all():
user = profile.user
bits = [user.id, user.username, user.first_name, user.last_name,
user.email, profile.phone, profile.occupation,
profile.departement, profile.type_cotiz]
bits = [
user.id,
user.username,
user.first_name,
user.last_name,
user.email,
profile.phone,
profile.occupation,
profile.departement,
profile.type_cotiz,
]
writer.writerow([str(bit) for bit in bits])
return response
@ -608,18 +666,24 @@ MEGA_ORGA = "Orga"
def csv_export_mega(filename, qs):
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename=' + filename
response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = "attachment; filename=" + filename
writer = unicodecsv.writer(response)
for reg in qs.all():
user = reg.user
profile = user.profile
comments = "---".join(
[comment.content for comment in reg.comments.all()])
bits = [user.username, user.first_name, user.last_name, user.email,
profile.phone, user.id,
profile.comments if profile.comments else "", comments]
comments = "---".join([comment.content for comment in reg.comments.all()])
bits = [
user.username,
user.first_name,
user.last_name,
user.email,
profile.phone,
user.id,
profile.comments if profile.comments else "",
comments,
]
writer.writerow([str(bit) for bit in bits])
@ -628,9 +692,9 @@ def csv_export_mega(filename, qs):
@buro_required
def export_mega_remarksonly(request):
filename = 'remarques_mega_{}.csv'.format(MEGA_YEAR)
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename=' + filename
filename = "remarques_mega_{}.csv".format(MEGA_YEAR)
response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = "attachment; filename=" + filename
writer = unicodecsv.writer(response)
event = Event.objects.get(title=MEGA_EVENT_NAME)
@ -639,8 +703,16 @@ def export_mega_remarksonly(request):
reg = val.registration
user = reg.user
profile = user.profile
bits = [user.username, user.first_name, user.last_name, user.email,
profile.phone, profile.id, profile.comments, val.content]
bits = [
user.username,
user.first_name,
user.last_name,
user.email,
profile.phone,
profile.id,
profile.comments,
val.content,
]
writer.writerow([str(bit) for bit in bits])
return response
@ -672,7 +744,7 @@ def export_mega_orgas(request):
qs = EventRegistration.objects.filter(event=event).filter(
options__id=participant_type
)
return csv_export_mega('orgas_mega_{}.csv'.format(MEGA_YEAR), qs)
return csv_export_mega("orgas_mega_{}.csv".format(MEGA_YEAR), qs)
@buro_required
@ -683,15 +755,15 @@ def export_mega_participants(request):
qs = EventRegistration.objects.filter(event=event).filter(
options__id=participant_type
)
return csv_export_mega('conscrits_mega_{}.csv'.format(MEGA_YEAR), qs)
return csv_export_mega("conscrits_mega_{}.csv".format(MEGA_YEAR), qs)
@buro_required
def export_mega(request):
event = Event.objects.filter(title=MEGA_EVENT_NAME)
qs = EventRegistration.objects.filter(event=event) \
.order_by("user__username")
return csv_export_mega('all_mega_{}.csv'.format(MEGA_YEAR), qs)
qs = EventRegistration.objects.filter(event=event).order_by("user__username")
return csv_export_mega("all_mega_{}.csv".format(MEGA_YEAR), qs)
# ------------------------------
# Fin des exports Mega hardcodés
@ -706,32 +778,28 @@ def utile_cof(request):
@buro_required
def utile_bda(request):
tirages = Tirage.objects.all()
return render(request, "utile_bda.html", {'tirages': tirages})
return render(request, "utile_bda.html", {"tirages": tirages})
@buro_required
def liste_bdadiff(request):
titre = "BdA diffusion"
personnes = CofProfile.objects.filter(mailing_bda=True, is_cof=True).all()
return render(request, "liste_mails.html",
{"titre": titre, "personnes": personnes})
return render(request, "liste_mails.html", {"titre": titre, "personnes": personnes})
@buro_required
def liste_bdarevente(request):
titre = "BdA revente"
personnes = CofProfile.objects.filter(mailing_bda_revente=True,
is_cof=True).all()
return render(request, "liste_mails.html", {"titre": titre,
"personnes": personnes})
personnes = CofProfile.objects.filter(mailing_bda_revente=True, is_cof=True).all()
return render(request, "liste_mails.html", {"titre": titre, "personnes": personnes})
@buro_required
def liste_diffcof(request):
titre = "Diffusion COF"
personnes = CofProfile.objects.filter(mailing_cof=True, is_cof=True).all()
return render(request, "liste_mails.html", {"titre": titre,
"personnes": personnes})
return render(request, "liste_mails.html", {"titre": titre, "personnes": personnes})
@cof_required
@ -740,7 +808,7 @@ def calendar(request):
instance = CalendarSubscription.objects.get(user=request.user)
except CalendarSubscription.DoesNotExist:
instance = None
if request.method == 'POST':
if request.method == "POST":
form = CalendarForm(request.POST, instance=instance)
if form.is_valid():
subscription = form.save(commit=False)
@ -749,19 +817,26 @@ def calendar(request):
subscription.token = uuid.uuid4()
subscription.save()
form.save_m2m()
messages.success(request,
"Calendrier mis à jour avec succès.")
return render(request, "gestioncof/calendar_subscription.html",
{'form': form,
'token': str(subscription.token)})
messages.success(request, "Calendrier mis à jour avec succès.")
return render(
request,
"gestioncof/calendar_subscription.html",
{"form": form, "token": str(subscription.token)},
)
else:
messages.error(request, "Formulaire incorrect.")
return render(request, "gestioncof/calendar_subscription.html",
{'form': form})
return render(
request, "gestioncof/calendar_subscription.html", {"form": form}
)
else:
return render(request, "gestioncof/calendar_subscription.html",
{'form': CalendarForm(instance=instance),
'token': instance.token if instance else None})
return render(
request,
"gestioncof/calendar_subscription.html",
{
"form": CalendarForm(instance=instance),
"token": instance.token if instance else None,
},
)
def calendar_ics(request, token):
@ -769,33 +844,33 @@ def calendar_ics(request, token):
shows = subscription.other_shows.all()
if subscription.subscribe_to_my_shows:
shows |= Spectacle.objects.filter(
attribues__participant__user=subscription.user,
tirage__active=True)
attribues__participant__user=subscription.user, tirage__active=True
)
shows = shows.distinct()
vcal = Calendar()
site = Site.objects.get_current()
for show in shows:
vevent = Vevent()
vevent.add('dtstart', show.date)
vevent.add('dtend', show.date + timedelta(seconds=7200))
vevent.add('summary', show.title)
vevent.add('location', show.location.name)
vevent.add('uid', 'show-{:d}-{:d}@{:s}'.format(
show.pk, show.tirage_id, site.domain))
vevent.add("dtstart", show.date)
vevent.add("dtend", show.date + timedelta(seconds=7200))
vevent.add("summary", show.title)
vevent.add("location", show.location.name)
vevent.add(
"uid", "show-{:d}-{:d}@{:s}".format(show.pk, show.tirage_id, site.domain)
)
vcal.add_component(vevent)
if subscription.subscribe_to_events:
for event in Event.objects.filter(old=False).all():
vevent = Vevent()
vevent.add('dtstart', event.start_date)
vevent.add('dtend', event.end_date)
vevent.add('summary', event.title)
vevent.add('location', event.location)
vevent.add('description', event.description)
vevent.add('uid', 'event-{:d}@{:s}'.format(
event.pk, site.domain))
vevent.add("dtstart", event.start_date)
vevent.add("dtend", event.end_date)
vevent.add("summary", event.title)
vevent.add("location", event.location)
vevent.add("description", event.description)
vevent.add("uid", "event-{:d}@{:s}".format(event.pk, site.domain))
vcal.add_component(vevent)
response = HttpResponse(content=vcal.to_ical())
response['Content-Type'] = "text/calendar"
response["Content-Type"] = "text/calendar"
return response
@ -823,7 +898,7 @@ class ConfigUpdate(FormView):
class UserAutocomplete(Select2QuerySetView):
model = User
search_fields = ('username', 'first_name', 'last_name')
search_fields = ("username", "first_name", "last_name")
user_autocomplete = buro_required(UserAutocomplete.as_view())

View file

@ -1,5 +1,5 @@
from django.forms.widgets import Widget
from django.forms.utils import flatatt
from django.forms.widgets import Widget
from django.utils.safestring import mark_safe
@ -13,8 +13,8 @@ class TriStateCheckbox(Widget):
def render(self, name, value, attrs=None, choices=()):
if value is None:
value = 'none'
attrs['value'] = value
value = "none"
attrs["value"] = value
final_attrs = self.build_attrs(self.attrs, attrs)
output = ["<span class=\"tristate\"%s></span>" % flatatt(final_attrs)]
return mark_safe('\n'.join(output))
output = ['<span class="tristate"%s></span>' % flatatt(final_attrs)]
return mark_safe("\n".join(output))