Code source de note.forms

#!/usr/bin/env python
# -*- encoding: utf-8 -*-

from django import forms
from django.forms import widgets
from django.contrib.admin import widgets as admin_widgets
from django.utils.safestring import mark_safe

import settings

import re
import time
import datetime

#: Import pour la traduction
from django.utils.translation import ugettext_lazy as _


[docs]class CreditRetraitWithoutIdbde(Exception): """Erreur levée en cas de tentative de crédit ou retrait sans idbde.""" pass
[docs]class BootstrapForm(forms.Form): """Ajoute l'attribut 'class' à tous les champs du formulare""" def __init__(self, *args, **kwargs): super(BootstrapForm, self).__init__(*args, **kwargs) for name, field in self.fields.items(): if field.widget.__class__ == widgets.RadioSelect: if field.widget.attrs.has_key('class'): field.widget.attrs['class'] += ' radio-inline' else: field.widget.attrs.update({'class': 'radio-inline'}) elif field.widget.__class__ == widgets.CheckboxSelectMultiple: if field.widget.attrs.has_key('class'): field.widget.attrs['class'] += ' checkbox-inline' else: field.widget.attrs.update({'class': 'checkbox-inline'}) elif field.widget.__class__ == widgets.CheckboxInput: if field.widget.attrs.has_key('class'): field.widget.attrs['class'] += ' checkbox-inline' else: field.widget.attrs.update({'class': 'checkbox-inline'}) else: if field.widget.attrs.has_key('class'): field.widget.attrs['class'] += ' form-control' else: field.widget.attrs.update({'class': 'form-control'})
[docs]class LoginForm(BootstrapForm): """Formulaire de login""" username = forms.CharField(label=_(u"Pseudo"), widget=forms.TextInput(attrs={"placeholder" : _(u"Ton pseudo note kfet"), "autofocus" : "autofocus", "class" : "form-control"})) password = forms.CharField(label=_(u"Mot de passe"), widget=forms.PasswordInput(render_value=False, attrs={ "class" : "form-control" })) droits = forms.ChoiceField(label=_(u"Droits"), choices=[(k, settings.ACL_MASKS[k][0]) for k in settings._acl_masks_keys])
[docs]class InviteForm(BootstrapForm): """Formulaire d'invitation""" nom = forms.CharField(label=_(u"Nom")) prenom = forms.CharField(label=_(u"Prénom"))
[docs]class FrenchFloatField(forms.FloatField): """Un champ FloatField, mais qui accepte aussi la virgule comme séparateur"""
[docs] def to_python(self, raw_value): """Conversion de la valeur texte en objet python.""" return super(FrenchFloatField, self).to_python(raw_value.replace(",", "."))
[docs]class BaseCompteRelatedForm(BootstrapForm): """Classe de base pour tous les formulaires traitant un compte (même une préinscription)""" type = forms.ChoiceField(label=_(u"Type de compte"), choices=[("personne", _(u"Personne")), ("club", _(u"Club"))]) nom = forms.CharField(label=_(u"Nom"), widget=forms.TextInput(attrs={"autocomplete" : "off"})) prenom = forms.CharField(label=_(u"Prénom"), widget=forms.TextInput(attrs={"autocomplete" : "off"})) mail = forms.CharField(label=_(u"E-mail"), widget=forms.TextInput(attrs={"autocomplete" : "off"})) tel = forms.CharField(label=_(u"Téléphone"), required=False, widget=forms.TextInput(attrs={"autocomplete" : "off"})) adresse = forms.CharField(label=_(u"Adresse"), required=False, widget=forms.TextInput(attrs={"autocomplete" : "off"})) pbsante = forms.CharField(label=_(u"Problèmes de santé"), required=False, widget=forms.TextInput(attrs={"autocomplete" : "off"})) normalien = forms.BooleanField(label=_(u"Normalien"), required=False) section = forms.CharField(label=_(u"Section"), required=False, widget=forms.TextInput(attrs={"autocomplete" : "off"}))
[docs]class Regen_pwForm(BootstrapForm): """Formulaire de demande de nouveau mot de passe""" nom = forms.CharField(label=_(u"Nom"), widget=forms.TextInput(attrs={"autocomplete" : "off", "class" : "form-control"}), required=True) prenom = forms.CharField(label=_(u"Prénom"), widget=forms.TextInput(attrs={"autocomplete" : "off", "class" : "form-control" }), required=True) mail = forms.CharField(label=_(u"E-mail"), widget=forms.TextInput(attrs={"autocomplete" : "off", "class" : "form-control" }), required=True)
[docs]class CompteRelatedForm(BaseCompteRelatedForm): """Classe de base pour les formulaires traitant un compte avec toutes ses données (par opposition à une préinscription).""" pseudo = forms.CharField(label=_(u"Pseudo"), widget=forms.TextInput(attrs={"autocomplete" : "off"})) fonction = forms.CharField(label=_(u"Fonction"), required=False, widget=forms.TextInput(attrs={"autocomplete" : "off"})) commentaire = forms.CharField(label=_(u"Commentaire"), required=False, widget=forms.TextInput(attrs={"autocomplete" : "off"})) report_period = forms.IntegerField(label=_(u"Fréquence des rapports"), required=False, widget=forms.TextInput(attrs={"autocomplete" : "off"})) bloque = forms.BooleanField(label=_(u"Bloquer le compte"), required=False) def __init__(self, *args, **kwargs): super(CompteRelatedForm, self).__init__(*args, **kwargs) # La section n'est facultative qu'à la préinscription self.fields["section"].required = True
[docs]class CompteForm(CompteRelatedForm): """Formulaire pour modifier un compte"""
[docs] def clean(self): """Supprime les None des champs facultatifs""" out = super(CompteForm, self).clean() if out.has_key("report_period") and (out["report_period"] == None): del out["report_period"] return out
[docs]class PreinscriptionForm(BaseCompteRelatedForm): """Formulaire de préinscription"""
[docs] def clean(self): """Gestion des None""" out = super(PreinscriptionForm, self).clean() return out
[docs]class ReadhesionForm(BootstrapForm): """Formulaire de réadhésion""" section = forms.CharField(label=_(u"Section"), required=True, widget=forms.TextInput(attrs={"autocomplete" : "off"})) # partie paiement on_note = FrenchFloatField(label=_(u"Montant supplémentaire à mettre sur la note"), required=False, initial=0, widget=forms.TextInput(attrs={"autocomplete" : "off"})) type_de_paiement = forms.ChoiceField(label=_(u"Type de paiement"), choices=[("none", _(u"Pas de crédit")), ("cheque", _(u"Chèque")), ("especes", _(u"Espèces")), ("cb", _(u"Carte bancaire")), ("virement", _(u"Virement bancaire")), ("soge", _(u"Société Générale"))] ) pay_nom = forms.CharField(label=_(u"Nom¹ (pour le paiement)"), required=False, widget=forms.TextInput(attrs={"autocomplete" : "off"})) pay_prenom = forms.CharField(label=_(u"Prénom¹ (pour le paiement)"), required=False, widget=forms.TextInput(attrs={"autocomplete" : "off"})) pay_banque = forms.CharField(label=_(u"Banque¹ (pour le paiement)"), required=False, widget=forms.TextInput(attrs={"autocomplete" : "off"}))
[docs] def clean(self): """des valeurs par défaut""" out = super(ReadhesionForm, self).clean() # Si on débite l'adhésion sur note sans faire de crédit, on n'autorise pas de on_note if out.get("type_de_paiement") == "none" and out.get("on_note", 0) != 0: raise forms.ValidationError(_(u"Pour une réadhésion sans crédit, il est impossible de donner un montant supplémentaire.")) if out.has_key("on_note") and type(out["on_note"]) == float: out["on_note"] = int(round(100 * out["on_note"])) else: out["on_note"] = 0 if out.has_key("section") and (out["section"] == ""): del out["section"] return out
[docs]class InscriptionForm(CompteRelatedForm): """Formulaire pour inscrire un nouveau compte. À l'initialisation, on peut fournir ``full_acl`` (keyword argument) pour passer en readonly les champs qui nécessite certains droits. Si non précisé, on considèrera que l'utilisateur n'a aucun droit. (Donc tous les champs sensibles seront readonly). """ #: Champs qui apparaîtront en premier PRIORITY_FIELDS = ["normalien", "wei", "override_adh"] #: Association champ -> droit nécessaire pour le modifier ACL_NEEDED = {"override_adh" : u"adhesions_admin"} wei = forms.BooleanField(label=_(u"Inscription au WEI"), required=False) override_adh = FrenchFloatField(label=_(u"Montant de l'adhésion"), widget=forms.TextInput(attrs={"autocomplete": "off"})) annee = forms.IntegerField(label=_(u"Année d'inscription (année courante si non précisée)"), required=False, widget=forms.TextInput(attrs={"autocomplete" : "off"})) # partie paiement on_note = FrenchFloatField(label=_(u"Montant supplémentaire à mettre sur la note"), required=False, initial=0, widget=forms.TextInput(attrs={"autocomplete" : "off"})) type_de_paiement = forms.ChoiceField(label=_(u"Type de paiement"), choices=[("cheque", _(u"Chèque")), ("especes", _(u"Espèces")), ("cb", _(u"Carte bancaire")), ("virement", _(u"Virement bancaire")), ("soge", _(u"Société Générale")), ("none", _(u"Pas de crédit"))] ) pay_nom = forms.CharField(label=_(u"Nom¹ (pour le paiement)"), required=False, widget=forms.TextInput(attrs={"autocomplete" : "off"})) pay_prenom = forms.CharField(label=_(u"Prénom¹ (pour le paiement)"), required=False, widget=forms.TextInput(attrs={"autocomplete" : "off"})) pay_banque = forms.CharField(label=_(u"Banque¹ (pour le paiement)"), required=False, widget=forms.TextInput(attrs={"autocomplete" : "off"})) def __init__(self, *args, **kwargs): acls = kwargs.get("full_rights", []) if acls: del kwargs["full_rights"] super(InscriptionForm, self).__init__(*args, **kwargs) # On bloque les champs que l'utilisateur n'a pas le droit de modifier for (fieldname, acl) in InscriptionForm.ACL_NEEDED.iteritems(): if not acl in acls: self.fields[fieldname].widget.attrs["readonly"] = True # Certains champs doivent recalculer le montant de l'adhésion en cas de modification for fieldname in ["normalien", "wei"]: self.fields[fieldname].widget.attrs["onChange"] = "javascript:update_montant_adhesion();return(false);"
[docs] def clean(self): """des valeurs par défaut""" out = super(InscriptionForm, self).clean() if out.has_key("on_note") and type(out["on_note"]) == float: out["on_note"] = int(round(100 * out["on_note"])) else: out["on_note"] = 0 if out.has_key("report_period") and (out["report_period"] == None): del out["report_period"] if out.has_key("annee") and (out["annee"] == None): del out["annee"] return out
[docs]class AliasForm(BootstrapForm): """Formulaire pour ajouter un alias""" alias = forms.CharField(label=_(u"Nouvel alias"), widget=forms.TextInput(attrs={"autocomplete" : "off"}))
[docs]class PasswordForm(BootstrapForm): """Formulaire pour changer un mot de passe""" password = forms.CharField(label=_(u"Nouveau mot de passe"), widget=forms.PasswordInput) password_confirm = forms.CharField(label=_(u"Confirmation du mot de passe"), widget=forms.PasswordInput, required=True)
[docs] def clean(self): """Vérifie que le mot de passe et sa confirmation concordent et enlève le deuxième.""" out = super(PasswordForm, self).clean() if self.errors: return out if (out["password"] != out["password_confirm"]): raise forms.ValidationError(_(u"Le mot de passe et sa confirmation ne concordent pas.")) else: del out["password_confirm"] return out
[docs]class SearchForm(CompteRelatedForm): """Formulaire pour faire une recherche avancée""" # On peut rechercher sur tous les champs d'un compte, plus les alias et les anciens pseudos alias = forms.CharField(label=_(u"Alias"), required=False, widget=forms.TextInput(attrs={"autocomplete" : "off"})) historique = forms.CharField(label=_(u"Ancien pseudo (toujours actif)"), required=False, widget=forms.TextInput(attrs={"autocomplete" : "off"})) def __init__(self, *args, **kwargs): super(CompteRelatedForm, self).__init__(*args, **kwargs) # La section n'est facultative qu'à la préinscription for field in self.fields: self.fields[field].required = False
[docs]class SearchHistoriquePseudoForm(BootstrapForm): """Formulaire pour faire une recherche par ancien pseudo""" historique = forms.CharField(label=_(u"Ancien pseudo"), widget=forms.TextInput(attrs={"autocomplete" : "off"})) exactfilter = forms.ChoiceField(label=_(u"Correspondance sur :"), widget=forms.RadioSelect, choices=[("b", _(u"début du mot")), ("", _(u"n'importe où dans le mot")), ("x", _(u"correspondance exacte"))], required=False)
# Les formats de timestamps acceptés # Les dates ### Les %s sont là parce qu'on a besoin de placer les groupes à deux endroits ### dans la regexp, et ça nécessite qu'ils aient deux noms différents year_match = ur'(?P<y%s>[0-9]{2}|[0-9]{4})' ym1, ym2 = year_match % (1), year_match % (2) month_match = ur'(?P<m%s>[0-9]{1,2})' mm1, mm2 = month_match % (1), month_match % (2) day_match = ur'(?P<d%s>[0-9]{1,2})' dm1, dm2 = day_match % (1), day_match % (2) date_match = ur'^(?:' + ym1 + '-' + mm1 + '-' + dm1 + '|' + dm2 + '/' + mm2 + '(?:/' + ym2 + ')?)$' date_matcher = re.compile(date_match) # Les heures (il n'est pas nécessaire de les spécifier (défaut à 0 pour chaque champ)) hours_match = ur'(?:(?P<H>[0-9]{1,2}) ?(?:h|heures?)?\.?)?' minutes_match = ur'(?:(?P<M>[0-9]{1,2}) ?(?:m|min|minutes?)?\.?)?' seconds_match = ur'(?:(?P<S>[0-9]{1,2}) ?(?:s|sec|secondes?)?\.?)?' separator_match = u"(?::| )?" time_match = u'^' + separator_match.join([hours_match, minutes_match, seconds_match]) + u'$' time_matcher = re.compile(time_match)
[docs]def to_string_ignoring_None(obj): """Convertit un objet en chaîne de caractères, mais avec None -> '' """ if (obj == None): return "" else: return unicode(obj)
[docs]def to_int_ignoring_null(obj): """Convertit un objet en entier mais avec None et '' -> 0""" if obj in [None, '']: return 0 else: return int(obj)
[docs]class MyDateField(forms.CharField): """Un champ personnalisé de détection de date. Renvoie un objet datetime.date""" description = u"Une date (assez souple sur le format d'entrée)"
[docs] def to_python(self, raw_value): """Conversion de la valeur texte en objet python.""" raw_value = super(MyDateField, self).to_python(raw_value) if raw_value == u'': raise forms.ValidationError(_(u"Ce champ est obligatoire")) result = date_matcher.match(raw_value) if result: data = result.groupdict() # On commence par fusionner ce qui n'a été séparé que par les règles de re.compile for field in ["y", "m", "d"]: f1, f2 = field + "1", field + "2" data[field] = to_string_ignoring_None(data[f1]) + to_string_ignoring_None(data[f2]) del data[f1], data[f2] # On convertit tout ce beau monde en entier for f in data.keys(): data[f] = to_int_ignoring_null(data[f]) # Si on n'a pas d'année, on met l'année en cours (plus un si la date se retrouve dans le passé) if (data["y"] == 0): today = time.localtime()[:3] if today[1:] > (data["m"], data["d"]): data["y"] = today[0] + 1 else: data["y"] = today[0] # Si la date a été donnée à deux chiffres, il faut la passer à 4 if data["y"] < 1000: if data["y"] > settings.YEAR_1900s_OVER: data["y"] += 1900 else: data["y"] += 2000 # On le renvoie sous forme de datetime.date try: return datetime.date(data["y"], data["m"], data["d"]) except Exception as exc: raise forms.ValidationError(_(u'"%s" ne peut pas être transformé en date : "%s"') % (raw_value, exc)) return data else: raise forms.ValidationError(u'"%s" ne peut pas être transformé en date (essaye le format JJ/MM/AAAA)' % (raw_value))
[docs]class MyTimeField(forms.CharField): """Un champ personnalisé de détection d'heure. Renvoie un objet datetime.time""" description = u"Une heure (assez souple sur le format d'entrée)"
[docs] def to_python(self, raw_value): """Conversion de la valeur texte en objet python.""" raw_value = super(MyTimeField, self).to_python(raw_value) result = time_matcher.match(raw_value) if result: data = result.groupdict() # On convertit tout ce beau monde en entier for f in data.keys(): data[f] = to_int_ignoring_null(data[f]) # On le renvoie sous forme de datetime.time try: return datetime.time(data["H"], data["M"], data["S"]) except Exception as exc: raise forms.ValidationError(u'"%s" ne peut pas être transformé en heure : "%s"' % (raw_value, exc)) else: raise forms.ValidationError(u'"%s" ne peut pas être transformé en heure (essaye le format HH:MM:SS)' % (raw_value))
[docs]class ActiviteForm(BootstrapForm): """Formulaire pour créer ou modifer une activité""" id = forms.IntegerField(required=False, widget=forms.HiddenInput) titre = forms.CharField(label=_(u"Titre"), widget=forms.TextInput(attrs={"autocomplete" : "off"})) signature = forms.CharField(label=_(u"Signature"), required=False, widget=forms.TextInput(attrs={"autocomplete" : "off"})) description = forms.CharField(label=_(u"Description"), required=False, widget=forms.TextInput(attrs={"autocomplete" : "off"})) debut_date = MyDateField(label=_(u"Date de début"), widget=forms.TextInput(attrs={"autocomplete" : "off"})) debut_time = MyTimeField(label=_(u"Heure de début"), widget=forms.TextInput(attrs={"autocomplete" : "off"})) fin_date = MyDateField(label=_(u"Date de fin"), widget=forms.TextInput(attrs={"autocomplete" : "off"})) fin_time = MyTimeField(label=_(u"Heure de fin"), widget=forms.TextInput(attrs={"autocomplete" : "off"})) lieu = forms.CharField(label=_(u"Lieu"), widget=forms.TextInput(attrs={"autocomplete" : "off"})) liste = forms.BooleanField(label=_(u"Liste d'invités"), required=False) listeimprimee = forms.BooleanField(label=_(u"Liste imprimée"), required=False)
[docs] def clean(self): """Récupère les données et fusionne les dates et les heures pour finalement donner une chaîne %Y-%m-%d %H:%M:%S""" out = super(ActiviteForm, self).clean() # à ce stade, out ne contient pas forcément tous les champs si certains ont été fournis vides if not set(["debut_date", "debut_time", "fin_date", "fin_time"]).issubset(out.keys()): return out dd, dt, fd, ft = out["debut_date"], out["debut_time"], out["fin_date"], out["fin_time"] debut, fin = datetime.datetime(dd.year, dd.month, dd.day, dt.hour, dt.minute, dt.second), datetime.datetime(fd.year, fd.month, fd.day, ft.hour, ft.minute, ft.second) if debut>fin: raise forms.ValidationError(_(u"La méthode DeLorean_TimeTravel() n'est pas encore implémentée dans la Note Kfet 2015, merci de réessayer plus tard ou bien de te résoudre à faire commencer ton activité avant qu'elle ne soit finie.")) else: del out["debut_date"], out["debut_time"], out["fin_date"], out["fin_time"] out["debut"], out["fin"] = debut.strftime("%Y-%m-%d %H:%M:%S"), fin.strftime("%Y-%m-%d %H:%M:%S") return out
def __init__(self, *args, **kwargs): """Pour initialiser le formulaire, on doit bidouiller un peu pour avoir debut_date, debut_time, fin_date et fin_time dans initial. Gère aussi les subtilités du champ listeimprimee. """ if 'initial' in kwargs.keys(): champs = kwargs['initial'].keys() for champ_date in ["debut", "fin"]: if champ_date in champs: date = time.strptime(kwargs['initial'][champ_date],"%Y-%m-%d %H:%M:%S") # On décompose le timestamp en date/heure kwargs['initial'][champ_date + "_date"], kwargs['initial'][champ_date + '_time'] = time.strftime("%d/%m/%Y %H:%M:%S", date).split(" ") del kwargs['initial'][champ_date] # Il faut aussi faire en sorte que le champ "liste imprimée" ne soit pas toujours disponible if 'listeimprimee' in kwargs.keys(): keeplisteimprimee = kwargs['listeimprimee'] del kwargs['listeimprimee'] else: keeplisteimprimee = True BootstrapForm.__init__(self, *args, **kwargs) if not keeplisteimprimee: del self.fields['listeimprimee']
[docs]class BoutonForm(BootstrapForm): """Formulaire pour créer ou modifier un bouton""" label = forms.CharField(label=_(u"Label"), widget=forms.TextInput(attrs={"autocomplete" : "off"})) montant = FrenchFloatField(label=_(u"Montant"), widget=forms.TextInput(attrs={"autocomplete" : "off"})) destinataire = forms.ChoiceField() categorie = forms.ChoiceField() affiche = forms.BooleanField(label=_(u"Afficher"), required=False, initial=True) description = forms.CharField(label=_(u"Description"), widget=forms.TextInput(attrs={"autocomplete" : "off"}), required=False) consigne = forms.BooleanField(label=_(u"Consigné"), required=False, initial=False) def __init__(self, *args, **kwargs): # Pour initialiser le formulaire, on veut que le solde soit passé en euros if 'initial' in kwargs.keys(): if 'montant' in kwargs['initial'].keys(): kwargs['initial']['montant'] = kwargs['initial']['montant'] / 100.0 BootstrapForm.__init__(self, *args, **kwargs)
[docs] def clean(self): """Vérifie que le bouton n'est pas absurde : montant >= 0, destinataire et montant ``int``.""" out = super(BoutonForm, self).clean() if out.has_key("montant") and out["montant"] < 0: raise forms.ValidationError(_(u"Le montant d'un bouton doit être positif")) try: out['destinataire'] = int(out['destinataire']) except: raise forms.ValidationError(_(u"Je sais pas comme tu t'es débrouillé pour pas me filer un entier dans le champ destinataire, et je veux pas le savoir.")) try: out['montant'] = int(round(100 * out['montant'])) except: raise forms.ValidationError(_(u"Précise un montant correct.")) return out
[docs]class PhotoForm(forms.Form): """Formulaire d'envoi de photo""" photo = forms.FileField(label=_(u"Fichier photo"))
[docs] def clean(self): """On n'autorise pas les photos trop grosses.""" out = super(PhotoForm, self).clean() if out.has_key("photo"): photo = out["photo"] if photo != None and photo.size > settings.MAX_PHOTO_SIZE: raise forms.ValidationError(u"Photo trop volumineuse (%s octets), maximum %s" % (photo.size, settings.MAX_PHOTO_SIZE)) return out
[docs]class MoneyForm(BootstrapForm): """Classe de base pour les formulaires qui parlent d'argent.""" montant = FrenchFloatField(label=_(u"Montant"), widget=forms.TextInput(attrs={"autocomplete" : "off"})) commentaire = forms.CharField(label=_(u"Commentaire"), required=False, widget=forms.TextInput(attrs={"autocomplete" : "off"}))
[docs] def clean(self): """Gère les conversions en centimes""" out = super(MoneyForm, self).clean() if out.has_key("montant") and type(out["montant"]) == float: out["montant"] = int(round(100 * out["montant"])) else: out["montant"] = 0 return out
[docs]class CreditRetraitForm(MoneyForm): """Formulaire pour effectuer un crédit ou un retrait.""" idbde = forms.IntegerField(widget=forms.HiddenInput) type = forms.ChoiceField(label=_(u"Type de paiement"), choices=[("", _(u"<Choisis un mode de paiement>")), ("especes", _(u"Espèces")), ("cb", _(u"Carte bancaire")), ("cheque", _(u"Chèque")), ("virement", _(u"Virement bancaire")),] ) nom = forms.CharField(label=_(u"Nom"), required=False, widget=forms.TextInput(attrs={"autocomplete" : "off"})) prenom = forms.CharField(label=_(u"Prénom"), required=False, widget=forms.TextInput(attrs={"autocomplete" : "off"})) banque = forms.CharField(label=_(u"Banque"), required=False)
[docs] def clean(self): """Gère le cas où le formulaire a été envoyé sans idbde (l'utilisateur n'a pas cliqué sur une note) en levant une erreur spécifique qui sera rattrappée lors de la gestion AJAJ.""" out = super(CreditRetraitForm, self).clean() if out.get("idbde") is None: raise CreditRetraitWithoutIdbde return out
[docs]class TransfertForm(MoneyForm): """Formulaire pour effectuer un transfert d'argent. Également utilisé pour les dons.""" commentaire = forms.CharField(label=_(u"Motif"), required=False, widget=forms.TextInput(attrs={"autocomplete" : "off"}))
[docs]class DeleteCompteForm(BootstrapForm): """Formulaire de confirmation de suppression de compte.""" anonymiser = forms.BooleanField(label=_(u"Anonymiser le compte (nom, prénom, mail, adresse, …)"), required=False)
DEPTS = ( ('', _(u'<Choisir un département>')), ('A0', _(u'Informatique (A0)')), ('A1', _(u'Mathématiques (A1)')), ('A2', _(u'Physique (A2)')), ('A\'2', _(u'Physique Appliquée (A\'2)')), ('A"2', _(u'Chimie (A"2)')), ('A3', _(u'Biologie (A3)')), ('B1234', _(u'SAPHIRE (B1234)')), ('B1', _(u'Mécanique (B1)')), ('B2', _(u'Génie Civil (B2)')), ('B3', _(u'Génie Mécanique (B3)')), ('B4', _(u'EEA (B4)')), ('C', _(u'Design (C)')), ('D2', _(u'Eco-Gestion (D2)')), ('D3', _(u'Sciences sociales (D3)')), ('E', _(u'Anglais (E)')), ('EXT', _(u'Autre (EXT)')), ) MODES_PAIEMENTS = ( ('note', _(u'Note')), ('soge', _(u'Société Générale')), ('virement', _(u'Virement Bancaire (sous conditions*)')), ) ROLES = ( ('libre', 'Electron libre'), ('chef_equipe', 'Chef d\'équipe (3A+)'), ('chef_bus', 'Chef de bus (2A sobre)'), ('staff', 'Staff WEI'), ('inconnu', 'Je ne sais pas'), ) BUS = ( ('Je ne sais pas', 'Je ne sais pas'), ('[Car]vengers' , '[Car]vengers'), ('Les Pirates du [Car]ibéen', 'Les Pirates du [Car]ibéen'), ('[Car] Toutatis !', '[Car] Toutatis !'), ('Chewbac[car]', 'Chewbac[car]'), ('Ex[car]libur', 'Ex[car]libur'), ('Zinedine Zi[Van]', 'Zinedine Zi[van]'), ('[Car]field', '[Car]field'), ('Staff', 'Staff'), )
[docs]class WEImonInscriptionForm(BootstrapForm): """ Questionnaire WEI pour les 2A+ """ tel = forms.CharField(max_length=10, required=True, label=_(u"Téléphone")) urgence_nom = forms.CharField(max_length=255, required=True, label=_(u"Nom de la personne à contacter en cas d'urgence")) urgence_tel = forms.CharField(max_length=10, required=True, label=_(u"Téléphone de la personne à contacter en cas d'urgence")) annee = forms.IntegerField(min_value=1, required=True, label=_(u"Années à l'ENS (année du WEI à venir incluse)")) dept = forms.ChoiceField(choices=DEPTS, required=True, initial='', label=_(u"Département d'enseignement")) paiement = forms.ChoiceField(choices=MODES_PAIEMENTS, required=True, initial='note', label=_(u"Mode de paiement")) normalien = forms.BooleanField(required=False, initial=False, label=_(u"Normalien, Prof, Directeur de département...")) conge = forms.BooleanField(required=False, initial=False, label=_(u"Si normalien : CIR, CST, Joker ...")) pbsante = forms.CharField(widget=forms.Textarea, required=False, label=_(u"Informations pour le staff WEI (allergies/intolérances, végétarien, problèmes de santé, ...)")) bus = forms.ChoiceField(choices=BUS, initial=_(u'Je ne sais pas'), required=True, label=_(u"Bus demandé")) role = forms.MultipleChoiceField(choices=ROLES, initial=[_(u'inconnu'),], required=True, label=_(u"Je voudrais faire mon WEI en tant que"), widget=forms.CheckboxSelectMultiple)
[docs] def _is_tel_number(self, data): """ Méthode permettant de vérifier qu'il s'agit bien d'un numéro de téléphone valide. """ raw_tel = data AUTH_PREFIXES = ['01', '02', '03', '04', '05', '06', '07', ] DELIMITERS = [" ", ".", "-", "_"] # Retrait des délimiteurs for delimiter in DELIMITERS: raw_tel = raw_tel.replace(delimiter, "") # On commence par vérifier que le numéro a la bonne taille if len(raw_tel) != 10: raise forms.ValidationError("Ton numéro de téléphone ne fait pas 10 chiffres") # Ensuite on vérifie le préfixe if not raw_tel[0:2] in AUTH_PREFIXES: raise forms.ValidationError("Ton numéro de téléphone est invalide") # Ensuite on vérifie qu'il s'agit bien d'un entier try: cleaned_tel = int(raw_tel) except ValueError: raise forms.ValidationError("Ton numéro de téléphone est invalide") return raw_tel
[docs] def clean_tel(self): """ Méthode effectuant les vérifications sur la validité du champ tel entré par l'utilisateur. """ return self._is_tel_number(self.cleaned_data["tel"])
[docs] def clean_urgence_tel(self): """ Méthode effectuant les vérifications sur la validité du champ urgence_tel entré par l'utilisateur. """ return self._is_tel_number(self.cleaned_data["urgence_tel"])
[docs] def clean_role(self): """ Méthode procédant à la validation du champ role, et le transformant en chaine de caractères afin de pouvoir être stocké dans la base de données. """ return ";".join(self.cleaned_data["role"])
[docs] def clean_conge(self): """ Méthode désactivant le champ conge si normalien n'est pas coché. """ val_norm = self.cleaned_data["normalien"] val_conge = self.cleaned_data["conge"] if val_norm == True: return val_conge else: return False
[docs] def clean(self): """ Vérifie que si le rôle 'staff' est demandé, alors le pseudo-bus staff est assigné de force. """ cleaned_data = super(WEImonInscriptionForm, self).clean() if 'role' not in cleaned_data: raise forms.ValidationError('Tu n\'as pas précisé quel rôle tu voulais') if 'staff' in cleaned_data['role']: cleaned_data['bus'] = 'Staff' return cleaned_data
[docs]class WEIVieuxForm(BootstrapForm): """ Questionnaire WEI pour les 2A+ """ pseudo = forms.CharField(max_length=255, required=True, label=_("Nom de note")) tel = forms.CharField(max_length=10, required=True, label=_(u"Téléphone"), widget=forms.TextInput(attrs={"autocomplete" : "off"})) mail = forms.EmailField(max_length=254, required=True, label=_(u"Email"), widget=forms.TextInput(attrs={"autocomplete" : "off"})) urgence_nom = forms.CharField(max_length=255, required=True, label=_(u"Nom de la personne à contacter en cas d'urgence"), widget=forms.TextInput(attrs={"autocomplete" : "off"})) urgence_tel = forms.CharField(max_length=10, required=True, label=_(u"Téléphone de la personne à contacter en cas d'urgence"), widget=forms.TextInput(attrs={"autocomplete" : "off"})) annee = forms.IntegerField(min_value=0, required=True, label=_(u"Années à l'ENS (année du WEI à venir incluse)")) dept = forms.ChoiceField(choices=DEPTS, required=True, initial='', label=_(u"Département d'enseignement")) paiement = forms.ChoiceField(choices=MODES_PAIEMENTS, required=True, label=_(u"Mode de paiement")) normalien = forms.BooleanField(required=False, initial=False, label=_(u"Normalien, Prof, Directeur de département...")) conge = forms.BooleanField(required=False, initial=False, label=_(u"Si normalien : CIR, CST ...")) pbsante = forms.CharField(widget=forms.Textarea(attrs={"autocomplete" : "off"}), required=False, label=_(u"Informations pour le staff WEI (allergies/intolérances, végétarien, problèmes de santé, ...)")) bus = forms.ChoiceField(choices=BUS, initial=_(u'Je ne sais pas'), required=True, label=_(u"Bus demandé")) role = forms.MultipleChoiceField(choices=ROLES, initial=[_(u'inconnu'),], required=True, label=_(u"Je voudrais faire mon WEI en tant que"), widget=forms.CheckboxSelectMultiple)
[docs] def _is_tel_number(self, data): """ Méthode permettant de vérifier qu'il s'agit bien d'un numéro de téléphone valide. """ raw_tel = data AUTH_PREFIXES = ['01', '02', '03', '04', '05', '06', '07', ] # On commence par vérifier que le numéro a la bonne taille if len(raw_tel) != 10: raise forms.ValidationError("Ton numéro de téléphone ne fait pas 10 chiffres") # Ensuite on vérifie le préfixe if not raw_tel[0:2] in AUTH_PREFIXES: raise forms.ValidationError("Ton numéro de téléphone est invalide") # Ensuite on vérifie qu'il s'agit bien d'un entier try: cleaned_tel = int(raw_tel) except ValueError: raise forms.ValidationError("Ton numéro de téléphone est invalide") return raw_tel
[docs] def clean_tel(self): """ Méthode effectuant les vérifications sur la validité du champ tel entré par l'utilisateur. """ return self._is_tel_number(self.cleaned_data["tel"])
[docs] def clean_urgence_tel(self): """ Méthode effectuant les vérifications sur la validité du champ urgence_tel entré par l'utilisateur. """ return self._is_tel_number(self.cleaned_data["urgence_tel"])
[docs] def clean_role(self): """ Méthode procédant à la validation du champ role, et le transformant en chaine de caractères afin de pouvoir être stocké dans la base de données. """ return ";".join(self.cleaned_data["role"])
[docs] def clean(self): """ Vérifie que si le rôle 'staff' est demandé, alors le pseudo-bus staff est assigné de force. """ cleaned_data = super(WEIVieuxForm, self).clean() if 'role' not in cleaned_data: raise forms.ValidationError('Tu n\'as pas précisé quel rôle tu voulais') if 'staff' in cleaned_data['role']: cleaned_data['bus'] = 'Staff' return cleaned_data
class WEIVieuxChangeForm(WEIVieuxForm): bus = forms.ChoiceField(choices=BUS, initial=_(u'Je ne sais pas'), required=True, label=_(u"Bus demandé")) Q_RADIO = [(i,i) for i in range(1,6)] Q_MOTS = ( ('0', _(u'Incognito')), ('1', _(u'Cape ou pas Cape')), ('2', _(u'Infatigable')), ('3', _(u'Tipiak')), ('4', _(u'Raspoutine')), ('5', _(u'Baie Des Cochons')), ('6', _(u'Licorne')), ('7', _(u'Soubassophone')), ('8', _(u' C\'est pas faux')), ('9', _(u'Tise')), ('10', _(u'Pas réflechir')), ('11', _(u'souillards')), ('12', _(u'canapé')), ('13', _(u'poulpe')), ('14', _(u'procrastination')), ('15', _(u'troll')), ('16', _(u'Garfield')), ('17', _(u'ricard')), ('18', _(u'Problèmes')), ('19', _(u'apéro')), ('20', _(u'colo')), ('21', _(u'banquet')), )
[docs]class WEI1AForm(BootstrapForm): """ Formulaire d'inscription au WEI pour 1A """ nom = forms.CharField(max_length=255, label=_(u"Nom")) prenom = forms.CharField(max_length=255, label=_(u"Prénom")) genre = forms.ChoiceField(label=_(u"Genre"), choices=[('', ''), ('M', _(u'Masculin')), ('F', _(u'Féminin')), ('N', _(u'Non-binaire')),]) adresse = forms.CharField(max_length=510, widget=forms.Textarea, label=_(u"Adresse")) mail = forms.EmailField(label=_(u"Adresse e-mail")) ml_evenements = forms.BooleanField(label=_(u"Inscription à la liste de diffusion pour être au courant des évènements du campus (1 mail/semaine)"), required=False) normalien = forms.BooleanField(label=_(u"Rémunéré(e) ?"), required=False) tel = forms.RegexField(max_length=15, regex='^\+?[0-9]{,15}$', label=_(u"Téléphone"), widget=forms.TextInput(attrs={"autocomplete" : "off"})) urgence_nom = forms.CharField(max_length=255, label=_(u"Personne à contacter en cas d'urgence"), widget=forms.TextInput(attrs={"autocomplete" : "off"})) urgence_tel = forms.RegexField(max_length=15, regex='^\+?[0-9]{,15}', label=_(u"Numéro à contacter en cas d'urgence"), widget=forms.TextInput(attrs={"autocomplete" : "off"})) dept = forms.ChoiceField(choices=DEPTS, label=_(u"Département d'enseignement envisagé")) note = forms.CharField(max_length=510, label=_(u"Nom de note")) infos = forms.CharField(max_length=510, widget=forms.Textarea, label=_(u"Infos importantes (Allergies, intolérances, -18ans, végétarien, problèmes de santé ...)"), required=False) soge = forms.BooleanField(required=False, label=_(u"Paiement du WEI avec la Société Générale ?")) #: Champs appartenant au questionnaire QUESTIONS = ["q_soirees", "q_alcools", "q_encadrement", "q_groupe", "q_sociable", "q_chants", "q_boire", "q_assos", "q_suiveur", "q_activites", "q_personnes", "q_conquetes", "q_mots", "petit_mot"] GENERALE = ["q_soirees", "q_alcools", "q_encadrement", "q_groupe", "q_sociable", "q_chants",] WEI = ["q_boire", "q_assos", "q_suiveur", "q_activites", "q_personnes", "q_conquetes",] MOTS = ["q_mots", "petit_mot"] # ouh, pour petit_mot, c'est un kludge de présentation. kludge-ception ! q_soirees = forms.ChoiceField(choices=Q_RADIO, widget=forms.RadioSelect(), label=_(u"J'aime bien aller en soirée"), help_text={'label_1' :_(u"Pas du tout"), 'label_5' : _(u"Énormément")}) q_alcools = forms.ChoiceField(choices=Q_RADIO, label=_(u"En soirée, je bois de l'alcool"), help_text={'label_1' :_(u"Pas du tout"), 'label_5' : _(u"Beaucoup")}, widget=forms.RadioSelect()) q_encadrement = forms.ChoiceField(choices=Q_RADIO, label=_(u"J'aime bien me sentir encadré"), help_text={'label_1' :_(u"Libre comme l'air"), 'label_5' : _(u"Je préfère me sentir bien entouré en sécurité")}, widget=forms.RadioSelect()) q_groupe = forms.ChoiceField(choices=Q_RADIO, label=_(u"J’aime bien être en groupe"), help_text={'label_1' :_(u"La solitude vaut mieux que la mauvaise compagnie"), 'label_5' : _(u"Plus on est, plus on rit")}, widget=forms.RadioSelect()) q_sociable = forms.ChoiceField(choices=Q_RADIO, label=_(u"Je vais facilement vers les autres"), help_text={'label_1' :_(u"Si on ne m'incite pas, je n'y vais pas"), 'label_5' : _(u"Je suis le plus sociable que je connaisse")}, widget=forms.RadioSelect()) q_chants = forms.ChoiceField(choices=Q_RADIO, label=_(u"Les chants"), help_text={'label_1' :_(u"J'ai la migraine"), 'label_5' : _(u"Je suis toujours celui qui crie le plus fort")}, widget=forms.RadioSelect()) q_boire = forms.ChoiceField(choices=Q_RADIO, label=_(u"Boire"), help_text={'label_1' :_(u"Non"), 'label_5' : _(u"Oui")}, widget=forms.RadioSelect()) q_assos = forms.ChoiceField(choices=Q_RADIO, widget=forms.RadioSelect(), label=_(u"Découvrir la vie associative du campus"), help_text={'label_1' :_(u"Non"), 'label_5' : _(u"Oui")}) q_activites = forms.ChoiceField(choices=Q_RADIO, widget=forms.RadioSelect(), label=_(u"Pour faire des activités"), help_text={'label_1' :_(u"Non"), 'label_5' : _(u"Oui")}) q_conquetes = forms.ChoiceField(choices=Q_RADIO, widget=forms.RadioSelect(), label=_(u"Pour flirter"), help_text={'label_1' :_(u"Non"), 'label_5' : _(u"Oui")}) q_personnes = forms.ChoiceField(choices=Q_RADIO, widget=forms.RadioSelect(), label=_(u"Pour rencontrer de nouvelles personnes"), help_text={'label_1' :_(u"Non"), 'label_5' : _(u"Oui")}) q_suiveur = forms.ChoiceField(choices=Q_RADIO, widget=forms.RadioSelect(), label=_(u"Enfin, je vais au WEI"), help_text={'label_1' :_(u"Parce que tout le monde y va…"), 'label_5' : _(u"J'en rêve depuis des mois !")}) q_mots = forms.MultipleChoiceField(choices=Q_MOTS, widget=widgets.CheckboxSelectMultiple(), label=_(u"Parmi ces mots, ceux qui te représentent le plus sont")) petit_mot = forms.CharField(max_length=510, widget=forms.Textarea, label=_(u"Un dernier petit mot, juste pour l'orga WEI ? (facultatif)"), required=False) INTRO_QUESTIONNAIRE = _(u""" Cher(e) première année, te voici devant un questionnaire qui n’est pas là pour te juger, te dissuader ni t’imposer une ambiance, mais pour faire en sorte que tu passes le meilleur week-end d’intégration possible. En effet, nous essayons d’adapter les ambiances en fonction des personnalités de chacun d’entre vous. La sincérité de tes réponses est donc de la plus grande importance. De plus, les réponses à ce questionnaire ne seront en aucun cas divulguées.""") OUTRO_QUESTIONNAIRE = _(u""" Merci beaucoup d’avoir pris le temps de répondre. Si tu as des inquiétudes ou des questions au sujet du week-end d’intégration, n’hésite pas à nous en faire part. Si tu souhaites le faire de manière anonyme tu peux envoyer un mail à <b>weiensc2017@gmail.com</b>. Durant toute la durée du WEI n’hésite pas à venir discuter avec tes chefs d’équipes et/ou le BDE si tu as des problèmes ou des questions.""") def clean_infos(self): data = self.cleaned_data['infos'] if data is None: data = '' return data return ';'.join(data) def clean_q_mots(self): data = self.cleaned_data['q_mots'] if len(data) != 3: raise forms.ValidationError(_(u"Choisis *3* mots !")) return ';'.join(data)
[docs]class WEIAdminForm(BootstrapForm): """ Formulaire pour la modification des paramètres du WEI """ wei_name = forms.CharField(max_length=1024, label=_(u"Nom du WEI"), required=True) wei_begin = forms.DateField(label=_(u"Date de début"), required=True) wei_end = forms.DateField(label=_(u"Date de fin"), required=True) wei_1a_available = forms.BooleanField(label=_(u"Inscriptions 1A ouvertes"), required=False) wei_vieux_available = forms.BooleanField(label=_(u"Inscriptions 2A+ ouvertes"), required=False) wei_contact = forms.EmailField(max_length=254, label=_(u"Adresse de contact"), required=True) prix_wei_normalien = forms.FloatField(label=_(u"Prix WEI normalien"), required=True) prix_wei_non_normalien = forms.FloatField(label=_(u"Prix WEI non normalien"), required=True) def clean_wei_begin(self): data = self.cleaned_data["wei_begin"] data = str(data) return data def clean_wei_end(self): data = self.cleaned_data["wei_end"] data = str(data) return data def clean_prix_wei_normalien(self): data = self.cleaned_data["prix_wei_normalien"] data = int(data*100) return data def clean_prix_wei_non_normalien(self): data = self.cleaned_data["prix_wei_non_normalien"] data = int(data*100) return data
[docs]class GenericDateTreasury(BootstrapForm): '''Un forms avec date et heure pour les requêtes SQL''' debut_date = MyDateField(label=_(u"Date de début"), widget=forms.TextInput(attrs={"autocomplete" : "off"})) fin_date = MyDateField(label=_(u"Date de fin"), widget=forms.TextInput(attrs={"autocomplete" : "off"}))
[docs]class AjoutRemise(BootstrapForm): """ Un forms avant pour corriger avant d'ajouter un transaction à une remise. """ nom = forms.CharField(max_length=255, required=True, label=_(u"Nom de l'émetteur"), widget=forms.TextInput(attrs={"autocomplete" : "off"})) prenom = forms.CharField(max_length=255, required=True, label=_(u"Prénom de l'émetteur")) banque = forms.CharField(max_length=255, required=True, label=_(u"Banque émettrice du chéquier")) montant = FrenchFloatField(min_value=0, required=True, label=_(u"Montant du chèque"), widget=forms.TextInput(attrs={"autocomplete" : "off"})) #idbde_beneficiaire = forms.IntegerField(required=False, widget=forms.HiddenInput) idremise = forms.ChoiceField(label =_(u"Remise")) def __init__(self, *args, **kwargs): # Pour initialiser le formulaire, on veut que le solde soit passé en euros if 'initial' in kwargs.keys(): if 'montant' in kwargs['initial'].keys(): kwargs['initial']['montant'] = kwargs['initial']['montant'] / 100.0 forms.Form.__init__(self, *args, **kwargs)
[docs] def clean(self) : """Vérifie que le chèque n'est pas absurde : montant >= 0, et montant ``int``.""" out = super(AjoutRemise, self).clean() if out.has_key("montant") and out["montant"] < 0: raise forms.ValidationError(_(u"Le montant d'un chèque doit être positif ou nul.")) try: out['montant'] = int(round(100 * out['montant'])) except: raise forms.ValidationError(_(u"Précise un montant correct.")) """Convertit l'idremise en ``int``""" if out.has_key("idremise"): out['idremise'] = int(out['idremise'])
_latex_escape = { '&' : r'\&', '%': r'\%', '$': r'\$', '#': r'\#', '_': r'\_', '{': r'\{', '}': r'\}', '[': r'\[', ']': r'\]', '~': r'\textasciitilde', '\\': r'\textbackslash', } def escape_latex(s): return "".join(_latex_escape.get(c, c) for c in s)
[docs]class FactureForm(BootstrapForm): """ Formulaire de création de facture""" nom = forms.CharField(max_length=255, required=True, label=_(u"Nom du client ou Raison sociale"), widget=forms.TextInput(attrs={'placeholder' : _(u"Mr. Dupont")})) adresse = forms.CharField(max_length=255, required=True, label=_(u"Adresse du client ou Domiciliation"), widget=forms.Textarea(attrs={'placeholder' : _(u"Chambre A123B, Etage 1 CROUS de Créteil 68 rue Camille Desmoulins 94230 CACHAN"), 'rows' : 5, 'style' : 'resize:none;'})) objet = forms.CharField(max_length=255, required=True, label=_(u"Objet"), widget=forms.TextInput(attrs={'placeholder' : _(u"Paiement Pot Design")})) description = forms.CharField(max_length=510, label=_(u"Description"), widget=forms.Textarea(attrs={'placeholder' : _(u'Cette facture concerne le paiement des boissons et de la nourriture de la soirée du 31 février 2015.'), 'style' : 'resize:none;'})) id_facture = forms.IntegerField(label=_(u"Facture n°"), widget=forms.TextInput(attrs={"autocomplete" : "off"}))
[docs] def clean(self): """ Formate les champs adresse et description pour le rendu dans le LaTeX.""" out = super(FactureForm, self).clean() out["objet"] = escape_latex(out["objet"]) out["nom"] = escape_latex(out["nom"]) if out.has_key("adresse"): out["adresse"] = out["adresse"].strip().split('\n') if out.has_key("description"): out["description"] = out["description"].strip().split('\n') return out
[docs]class ProduitForm(BootstrapForm): """ Formulaire pour un produit Un produit se compose : * designation = ``str`` * quantite = <decimal> * prixunitare = <decimal> """ designation = forms.CharField(max_length=255, label=_(u"Désignation")) quantite = forms.DecimalField(label=_(u"Quantité"), localize = True, widget=forms.TextInput(attrs={"autocomplete" : "off"})) prixunitaire = forms.DecimalField(label=_(u"Prix unitaire"), localize = True, widget=forms.TextInput(attrs={"autocomplete" : "off"})) def __init__(self, *args, **kwargs): super(ProduitForm, self).__init__(*args, **kwargs) self.empty_permitted = False
[docs] def clean(self): """Vérifie que le produit n'est pas absurde : quantite > 0 et prixunitaire >= 0.""" out = super(ProduitForm, self).clean() if out.has_key("quantite") and out["quantite"] <= 0: raise forms.ValidationError(_(u"La quantite d'un produit doit être positif et non nulle.")) out["prixtotal"] = out["quantite"] * out["prixunitaire"] return out
[docs]class QuestionForm(BootstrapForm): """ Formulaire représentant une question attendant une réponse de 1 à 5 """ label_1 = forms.CharField(widget=forms.HiddenInput(), help_text='Totalement faux') buttons = forms.ChoiceField(choices=[(i," ") for i in range(1,6)], required=True, widget=forms.RadioSelect()) label_5 = forms.CharField(widget=forms.HiddenInput(), help_text='Totalement vrai')