Grod commit dégueux avec une tonne de trucs. Berk.

This commit is contained in:
root 2014-08-19 12:54:22 +02:00
parent d5b3d3f958
commit 64b8ee4133
17 changed files with 536 additions and 209 deletions

View file

@ -55,6 +55,9 @@ class EventOptionChoiceInline(admin.TabularInline):
class EventOptionInline(admin.TabularInline):
model = EventOption
class EventCommentFieldInline(admin.TabularInline):
model = EventCommentField
class EventOptionAdmin(admin.ModelAdmin):
inlines = [
EventOptionChoiceInline,
@ -63,6 +66,7 @@ class EventOptionAdmin(admin.ModelAdmin):
class EventAdmin(admin.ModelAdmin):
inlines = [
EventOptionInline,
EventCommentFieldInline,
]
#from eav.forms import BaseDynamicEntityForm
@ -253,6 +257,8 @@ admin.site.register(EventOption, EventOptionAdmin)
admin.site.unregister(User)
admin.site.register(User, UserProfileAdmin)
admin.site.register(CofProfile)
admin.site.register(Club)
admin.site.register(CustomMail)
admin.site.register(PetitCoursSubject)
admin.site.register(PetitCoursAbility, PetitCoursAbilityAdmin)
admin.site.register(PetitCoursAttribution, PetitCoursAttributionAdmin)

View file

@ -24,6 +24,11 @@ TYPE_COTIZ_CHOICES = (
('exterieur', _(u"Extérieur")),
)
TYPE_COMMENT_FIELD = (
('text', _(u"Texte long")),
('char', _(u"Texte court")),
)
def choices_length (choices):
return reduce (lambda m, choice: max (m, len (choice[0])), choices, 0)
@ -45,6 +50,7 @@ class CofProfile(models.Model):
mailing_cof = models.BooleanField("Recevoir les mails COF", default = False)
mailing_bda = models.BooleanField("Recevoir les mails BdA", default = False)
mailing_bda_revente = models.BooleanField("Recevoir les mails de revente de places BdA", default = False)
comments = models.TextField("Commentaires visibles uniquement par le Buro", blank = True)
is_buro = models.BooleanField("Membre du Burô", default = False)
petits_cours_accept = models.BooleanField("Recevoir des petits cours", default = False)
petits_cours_remarques = models.TextField(_(u"Remarques et précisions pour les petits cours"),
@ -62,6 +68,24 @@ def create_user_profile(sender, instance, created, **kwargs):
CofProfile.objects.get_or_create(user = instance)
post_save.connect(create_user_profile, sender = User)
class Club(models.Model):
name = models.CharField("Nom", max_length = 200)
description = models.TextField("Description")
respos = models.ManyToManyField(User, related_name = "clubs_geres")
membres = models.ManyToManyField(User, related_name = "clubs")
class CustomMail(models.Model):
shortname = models.SlugField(max_length = 50, blank = False)
title = models.CharField("Titre", max_length = 200, blank = False)
content = models.TextField("Contenu", blank = False)
comments = models.TextField("Informations contextuelles sur le mail", blank = True)
class Meta:
verbose_name = "Mails personnalisables"
def __unicode__(self):
return u"%s: %s" % (self.shortname, self.title)
class Event(models.Model):
title = models.CharField("Titre", max_length = 200)
location = models.CharField("Lieu", max_length = 200)
@ -77,6 +101,23 @@ class Event(models.Model):
def __unicode__(self):
return unicode(self.title)
class EventCommentField(models.Model):
event = models.ForeignKey(Event, related_name = "commentfields")
name = models.CharField("Champ", max_length = 200)
fieldtype = models.CharField("Type", max_length = 10, choices = TYPE_COMMENT_FIELD, default = "text")
default = models.TextField("Valeur par défaut", blank = True)
class Meta:
verbose_name = "Champ"
def __unicode__(self):
return unicode(self.name)
class EventCommentValue(models.Model):
commentfield = models.ForeignKey(EventCommentField, related_name = "values")
registration = models.ForeignKey("EventRegistration", related_name = "comments")
content = models.TextField("Contenu", blank = True, null = True)
class EventOption(models.Model):
event = models.ForeignKey(Event, related_name = "options")
name = models.CharField("Option", max_length = 200)
@ -102,6 +143,7 @@ class EventRegistration(models.Model):
user = models.ForeignKey(User)
event = models.ForeignKey(Event)
options = models.ManyToManyField(EventOptionChoice)
filledcomments = models.ManyToManyField(EventCommentField, through = EventCommentValue)
paid = models.BooleanField("A payé", default = False)
class Meta:

View file

@ -60,38 +60,45 @@ def _get_attrib_counter(user, matiere):
counter.save()
return counter
@buro_required
def traitement(request, demande_id):
demande = get_object_or_404(PetitCoursDemande, id = demande_id)
if demande.niveau == "other":
return _traitement_other(request, demande)
if request.method == "POST":
return _traitement_post(request, demande)
proposals = {}
proposed_for = {}
unsatisfied = []
attribdata = {}
def _get_demande_candidates(demande, redo = False):
for matiere in demande.matieres.all():
candidates = PetitCoursAbility.objects.filter(matiere = matiere, niveau = demande.niveau)
candidates = candidates.filter(user__profile__is_cof = True,
user__profile__petits_cours_accept = True)
if demande.agrege_requis:
candidates = candidates.filter(agrege = True)
candidates = candidates.values_list('user', flat = True).distinct().order_by('?').all()
tuples = []
for candidate in candidates:
user = User.objects.get(pk = candidate)
tuples.append((candidate, _get_attrib_counter(user, matiere)))
if tuples:
if redo:
attributions = PetitCoursAttribution.objects.filter(demande = demande,
matiere = matiere).all()
for attrib in attributions:
candidates = candidates.exclude(user = attrib.user)
candidates = candidates.order_by('?').select_related().all()
yield (matiere, candidates)
@buro_required
def traitement(request, demande_id, redo = False):
demande = get_object_or_404(PetitCoursDemande, id = demande_id)
if demande.niveau == "other":
return _traitement_other(request, demande, redo)
if request.method == "POST":
return _traitement_post(request, demande)
proposals = {}
proposed_for = {}
unsatisfied = []
attribdata = {}
for matiere, candidates in _get_demande_candidates(demande, redo):
if candidates:
tuples = []
for candidate in candidates:
user = candidate.user
tuples.append((candidate, _get_attrib_counter(user, matiere)))
tuples = sorted(tuples, key = lambda c: c[1].count)
candidates, _ = zip(*tuples)
attribdata[matiere.id] = []
candidates = candidates[0:min(3, len(candidates))]
attribdata[matiere.id] = []
proposals[matiere] = []
for candidate in candidates:
user = User.objects.get(pk = candidate)
user = candidate.user
proposals[matiere].append(user)
attribdata[matiere.id].append(user.id)
if user not in proposed_for:
@ -100,10 +107,15 @@ def traitement(request, demande_id):
proposed_for[user].append(matiere)
else:
unsatisfied.append(matiere)
return _finalize_traitement(request, demande, proposals, proposed_for, unsatisfied, attribdata)
return _finalize_traitement(request, demande, proposals,
proposed_for, unsatisfied, attribdata, redo)
@buro_required
def retraitement(request, demande_id):
return traitement(request, demande_id, redo = True)
def _finalize_traitement(request, demande, proposals, proposed_for,
unsatisfied, attribdata, redo = False):
unsatisfied, attribdata, redo = False, errors = None):
proposals = proposals.items()
proposed_for = proposed_for.items()
attribdata = attribdata.items()
@ -121,6 +133,7 @@ def _finalize_traitement(request, demande, proposals, proposed_for,
"mainmail": mainmail,
"attribdata": base64.b64encode(simplejson.dumps(attribdata)),
"redo": redo,
"errors": errors,
})
def _generate_eleve_email(demande, proposed_for):
@ -130,15 +143,45 @@ def _generate_eleve_email(demande, proposed_for):
proposed_mails.append((user, msg))
return proposed_mails
def _traitement_other_post(request, demande):
for matiere in demande.matieres.all():
for choice_id in range(3):
choice = request.POST["proposal-%d-%d"] % (matiere.id, choice_id)]:
pass
return None
return _finalize_traitement(request, demande, proposals, proposed_for, unsatisfied, attribdata)
def _traitement_other_preparing(request, demande):
redo = "redo" in request.POST
unsatisfied = []
proposals = {}
proposed_for = {}
attribdata = {}
errors = []
for matiere, candidates in _get_demande_candidates(demande, redo):
if 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" % (matiere.id, choice_id)])
if choice == -1:
continue
if choice not in candidates:
errors.append(u"Choix invalide pour la proposition %d en %s" % (choice_id + 1, matiere))
continue
user = candidates[choice]
if user in proposals[matiere]:
errors.append(u"La proposition %d en %s est un doublon" % (choice_id + 1, matiere))
continue
proposals[matiere].append(user)
attribdata[matiere.id].append(user.id)
if user not in proposed_for:
proposed_for[user] = [matiere]
else:
proposed_for[user].append(matiere)
if not proposals[matiere]:
errors.append(u"Aucune proposition pour %s" % (matiere,))
elif len(proposals[matiere]) < 3:
errors.append(u"Seulement %d proposition%s pour %s" % (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)
def _traitement_other(request, demande):
def _traitement_other(request, demande, redo):
if request.method == "POST":
if "preparing" in request.POST:
return _traitement_other_preparing(request, demande)
@ -148,13 +191,7 @@ def _traitement_other(request, demande):
proposed_for = {}
unsatisfied = []
attribdata = {}
for matiere in demande.matieres.all():
candidates = PetitCoursAbility.objects.filter(matiere = matiere, niveau = demande.niveau)
candidates = candidates.filter(user__profile__is_cof = True,
user__profile__petits_cours_accept = True)
if demande.agrege_requis:
candidates = candidates.filter(agrege = True)
candidates = candidates.order_by('?').select_related().all()
for matiere, candidates in _get_demande_candidates(demande, redo):
if candidates:
tuples = []
for candidate in candidates:
@ -243,49 +280,6 @@ def _traitement_post(request, demande):
"redo": redo,
})
@buro_required
def retraitement(request, demande_id):
demande = get_object_or_404(PetitCoursDemande, id = demande_id)
if request.method == "POST":
return _traitement_post(request, demande)
if demande.niveau == "other":
return _traitement_other(request, demande)
proposals = {}
proposed_for = {}
unsatisfied = []
attribdata = {}
for matiere in demande.matieres.all():
candidates = PetitCoursAbility.objects.filter(matiere = matiere, niveau = demande.niveau)
candidates = candidates.filter(user__profile__is_cof = True,
user__profile__petits_cours_accept = True)
if demande.agrege_requis:
candidates = candidates.filter(agrege = True)
attributions = PetitCoursAttribution.objects.filter(demande = demande, matiere = matiere).all()
for attrib in attributions:
candidates = candidates.exclude(user = attrib.user)
candidates = candidates.values_list('user', flat = True).distinct().order_by('?').all()
tuples = []
for candidate in candidates:
user = User.objects.get(pk = candidate)
tuples.append((candidate, _get_attrib_counter(user, matiere)))
if tuples:
tuples = sorted(tuples, key = lambda c: c[1].count)
candidates, _ = zip(*tuples)
attribdata[matiere.id] = []
candidates = candidates[0:min(3, len(candidates))]
proposals[matiere] = []
for candidate in candidates:
user = User.objects.get(pk = candidate)
proposals[matiere].append(user)
attribdata[matiere.id].append(user.id)
if user not in proposed_for:
proposed_for[user] = [matiere]
else:
proposed_for[user].append(matiere)
else:
unsatisfied.append(matiere)
return _finalize_traitement(request, demande, proposals, proposed_for, unsatisfied, attribdata, redo = True)
class BaseMatieresFormSet(BaseInlineFormSet):
def clean(self):
super(BaseMatieresFormSet, self).clean()

View file

@ -2,9 +2,12 @@ from django.contrib.sites.models import Site
from django.conf import settings
from django_cas.backends import CASBackend, _verify as CASverify
from django_cas.models import User
from django.contrib.auth.models import User as DjangoUser
from django.db import models, connection
from django.core.mail import send_mail
from django.template import Template, Context
from gestioncof.models import CofProfile
from gestioncof.models import CofProfile, CustomMail
class COFCASBackend(CASBackend):
def authenticate_cas(self, ticket, service, request):
@ -76,3 +79,16 @@ def unlock_tables(*models):
return row
unlock_table = unlock_tables
def send_custom_mail(to, shortname, context = None, from_email = "cof@ens.fr"):
if context is None: context = {}
if isinstance(to, DjangoUser):
context["nom"] = to.get_full_name()
context["prenom"] = to.first_name
to = to.email
mail = CustomMail.objects.get(shortname = shortname)
template = Template(mail.content)
message = template.render(Context(context))
send_mail (mail.title, message,
from_email, [to],
fail_silently = True)

View file

@ -15,10 +15,11 @@ from django.contrib.auth.views import login as django_login_view
from gestioncof.models import Survey, SurveyQuestion, SurveyQuestionAnswer, SurveyAnswer
from gestioncof.models import Event, EventOption, EventOptionChoice, EventRegistration
from gestioncof.models import EventCommentField, EventCommentValue
from gestioncof.models import CofProfile, Clipper
from gestioncof.decorators import buro_required, cof_required
from gestioncof.widgets import TriStateCheckbox
from gestioncof.shared import lock_table, unlock_table
from gestioncof.shared import lock_table, unlock_table, send_custom_mail
@login_required
def home(request):
@ -205,6 +206,17 @@ def get_event_form_choices(event, form):
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)
if value == field.default:
continue
(storage, _) = EventCommentValue.objects.get_or_create(commentfield = field,
registration = registration)
storage.content = value
storage.save()
@login_required
def event(request, event_id):
event = get_object_or_404(Event, id = event_id)
@ -426,6 +438,7 @@ class RegistrationProfileForm(forms.ModelForm):
'mailing_cof',
'mailing_bda',
'mailing_bda_revente',
'comments'
]
def save(self, *args, **kw):
@ -445,7 +458,7 @@ class RegistrationProfileForm(forms.ModelForm):
class Meta:
model = CofProfile
fields = ("login_clipper", "num", "phone", "occupation", "departement", "is_cof", "type_cotiz", "mailing_cof", "mailing_bda", "mailing_bda_revente",)
fields = ("login_clipper", "num", "phone", "occupation", "departement", "is_cof", "type_cotiz", "mailing_cof", "mailing_bda", "mailing_bda_revente", "comments")
def registration_set_ro_fields(user_form, profile_form):
user_form.fields['username'].widget.attrs['readonly'] = True
@ -487,15 +500,16 @@ def registration_form(request, login_clipper = None, username = None):
return render(request, "registration_form.html", {"user_form": user_form, "profile_form": profile_form, "member": member, "login_clipper": login_clipper})
STATUS_CHOICES = (('no','Non'),
('wait','Attente paiement'),
('paid','Payé'),)
('wait','Oui mais attente paiement'),
('paid','Oui payé'),)
class AdminEventForm(forms.Form):
status = forms.ChoiceField(label = "Inscription", choices = STATUS_CHOICES, widget = RadioSelect)
def __init__(self, *args, **kwargs):
event = kwargs.pop("event")
self.event = event
current_choices = kwargs.pop("current_choices", None)
registration = kwargs.pop("current_registration", None)
current_choices = registration.options.all() if registration is not None else []
paid = kwargs.pop("paid", None)
if paid == True:
kwargs["initial"] = {"status":"paid"}
@ -505,12 +519,12 @@ class AdminEventForm(forms.Form):
kwargs["initial"] = {"status":"no"}
super(AdminEventForm, self).__init__(*args, **kwargs)
choices = {}
if current_choices:
for choice in current_choices.all():
if choice.event_option.id not in choices:
choices[choice.event_option.id] = [choice.id]
else:
choices[choice.event_option.id].append(choice.id)
comments = {}
for choice in current_choices:
if choice.event_option.id not in choices:
choices[choice.event_option.id] = [choice.id]
else:
choices[choice.event_option.id].append(choice.id)
all_choices = choices
for option in event.options.all():
choices = [(choice.id, choice.value) for choice in option.choices.all()]
@ -530,12 +544,31 @@ class AdminEventForm(forms.Form):
initial = initial)
field.option_id = option.id
self.fields["option_%d" % option.id] = field
for commentfield in event.commentfields.all():
initial = commentfield.default
if registration is not None:
try:
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)
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_'):
yield (self.fields[name].option_id, value)
def comments(self):
for name, value in self.cleaned_data.items():
if name.startswith('comment_'):
yield (self.fields[name].comment_id, value)
@buro_required
def registration_form2(request, login_clipper = None, username = None):
events = Event.objects.filter(old = False).all()
@ -571,7 +604,7 @@ def registration_form2(request, login_clipper = None, username = None):
for event in events:
try:
current_registration = EventRegistration.objects.get(user = member, event = event)
form = AdminEventForm(event = event, current_choices = current_registration.options, paid = current_registration.paid)
form = AdminEventForm(event = event, current_registration = current_registration, paid = current_registration.paid)
except EventRegistration.DoesNotExist:
form = AdminEventForm(event = event)
event_forms.append(form)
@ -618,10 +651,14 @@ def registration(request):
if user_form.is_valid() and profile_form.is_valid() and not any([not form.is_valid() for form in event_forms]):
member = user_form.save()
(profile, _) = CofProfile.objects.get_or_create(user = member)
was_cof = profile.is_cof
request_dict["num"] = profile.num
profile_form = RegistrationProfileForm(request_dict, instance = profile)
profile_form.is_valid()
profile_form.save()
(profile, _) = CofProfile.objects.get_or_create(user = member)
if profile.is_cof and not was_cof:
send_custom_mail(member, "bienvenue")
for form in event_forms:
if form.cleaned_data['status'] == 'no':
try:
@ -631,10 +668,18 @@ def registration(request):
pass
continue
all_choices = get_event_form_choices(form.event, form)
(current_registration, _) = EventRegistration.objects.get_or_create(user = member, event = form.event)
(current_registration, created_reg) = EventRegistration.objects.get_or_create(user = member, event = form.event)
update_event_form_comments(event, form, current_registration)
current_registration.options = all_choices
current_registration.paid = (form.cleaned_data['status'] == 'paid')
current_registration.save()
if event.title == "Mega 2014" and created_reg:
field = EventCommentField.objects.get(event = event, name = "Commentaires")
try:
comments = EventCommentValue.objects.get(commentfield = field, registration = current_registration).content
except EventCommentValue.DoesNotExist:
comments = field.default
send_custom_mail(member, "mega", {"remarques": comments})
success = True
return render(request, "registration_post.html", {"success": success, "user_form": user_form, "profile_form": profile_form, "member": member, "login_clipper": login_clipper, "event_forms": event_forms})
else:
@ -653,54 +698,76 @@ def export_members(request):
return response
def csv_export_mega(filename, qs):
response = HttpResponse(mimetype = 'text/csv')
response['Content-Disposition'] = 'attachment; filename=' + filename
writer = unicodecsv.UnicodeWriter(response)
for reg in qs.all():
user = reg.user
profile = user.get_profile()
comments = "---".join([comment.content for comment in reg.comments.all()])
bits = [user.username, user.first_name, user.last_name, user.email, profile.phone, profile.num, profile.comments if profile.comments else "", comments]
writer.writerow([unicode(bit) for bit in bits])
return response
@buro_required
def export_mega_remarksonly(request):
filename = 'remarques_mega_2014.csv'
response = HttpResponse(mimetype = 'text/csv')
response['Content-Disposition'] = 'attachment; filename=' + filename
writer = unicodecsv.UnicodeWriter(response)
event = Event.objects.get(title = "Mega 2014")
commentfield = event.commentfields.get(name = "Commentaires")
for val in commentfield.values.all():
reg = val.registration
user = reg.user
profile = user.get_profile()
bits = [user.username, user.first_name, user.last_name, user.email, profile.phone, profile.num, profile.comments, val.content]
writer.writerow([unicode(bit) for bit in bits])
return response
@buro_required
def export_mega_bytype(request, type):
types = {"orga-actif": "Orga actif",
"orga-branleur": "Orga branleur",
"conscrit-eleve": "Conscrit élève",
"conscrit-etudiant": "Conscrit étudiant"}
if type not in types:
raise Http404
event = Event.objects.get(title = "Mega 2014")
type_option = event.options.get(name = "Type")
participant_type = type_option.choices.get(value = types[type]).id
qs = EventRegistration.objects.filter(event = event).filter(options__id__exact = participant_type)
return csv_export_mega(type + '_mega_2014.csv', qs)
@buro_required
def export_mega_orgas(request):
response = HttpResponse(mimetype = 'text/csv')
response['Content-Disposition'] = 'attachment; filename=orgas_mega.csv'
writer = unicodecsv.UnicodeWriter(response)
event = Event.objects.get(title = "MEGA")
event = Event.objects.get(title = "Mega 2014")
type_option = event.options.get(name = "Type")
participant_type = type_option.choices.get(value = "Participant").id
for reg in EventRegistration.objects.filter(event = event).exclude(options__id__exact = participant_type).all():
user = reg.user
profile = user.get_profile()
bits = [user.username, user.first_name, user.last_name, user.email, profile.phone, profile.num]
writer.writerow([unicode(bit) for bit in bits])
participant_type_a = type_option.choices.get(value = "Conscrit étudiant").id
participant_type_b = type_option.choices.get(value = "Conscrit élève").id
qs = EventRegistration.objects.filter(event = event).exclude(options__id__in = (participant_type_a, participant_type_b))
return csv_export_mega('orgas_mega_2014.csv', qs)
return response
@buro_required
def export_mega_participants(request):
response = HttpResponse(mimetype = 'text/csv')
response['Content-Disposition'] = 'attachment; filename=participants_mega.csv'
writer = unicodecsv.UnicodeWriter(response)
event = Event.objects.get(title = "MEGA")
event = Event.objects.get(title = "Mega 2014")
type_option = event.options.get(name = "Type")
participant_type = type_option.choices.get(value = "Participant").id
for reg in EventRegistration.objects.filter(event = event).filter(options__id__exact = participant_type).all():
user = reg.user
profile = user.get_profile()
bits = [user.username, user.first_name, user.last_name, user.email, profile.phone, profile.num]
writer.writerow([unicode(bit) for bit in bits])
return response
participant_type_a = type_option.choices.get(value = "Conscrit étudiant").id
participant_type_b = type_option.choices.get(value = "Conscrit élève").id
qs = EventRegistration.objects.filter(event = event).filter(options__id__in = (participant_type_a, participant_type_b))
return csv_export_mega('participants_mega_2014.csv', qs)
@buro_required
def export_mega(request):
response = HttpResponse(mimetype = 'text/csv')
response['Content-Disposition'] = 'attachment; filename=all_mega.csv'
writer = unicodecsv.UnicodeWriter(response)
event = Event.objects.filter(title = "MEGA")
for reg in EventRegistration.objects.filter(event = event).order_by("user__username").all():
user = reg.user
profile = user.get_profile()
bits = [user.username, user.first_name, user.last_name, user.email, profile.phone, profile.num]
writer.writerow([unicode(bit) for bit in bits])
return response
event = Event.objects.filter(title = "Mega 2014")
qs = EventRegistration.objects.filter(event = event).order_by("user__username")
return csv_export_mega('all_mega_2014.csv', qs)
@buro_required
def utile_cof(request):