1 # -*- encoding: utf-8 -*-
3 from django
import forms
4 from django
.db
.models
import Q
5 from django
.forms
.models
import inlineformset_factory
6 from itertools
import chain
9 OUI_NON_CHOICES
= (('1', 'Oui'), ('0', 'Non'))
11 class PersonneForm(forms
.ModelForm
):
12 genre
= forms
.ChoiceField(widget
=forms
.RadioSelect(), choices
=GENRE_CHOICES
)
16 fields
= ('nom', 'prenom', 'courriel', 'genre')
18 class PersonneInscriptionForm(PersonneForm
):
19 password
= forms
.CharField(widget
=forms
.PasswordInput(), label
="Mot de passe")
20 password_confirmation
= forms
.CharField(widget
=forms
.PasswordInput(), label
="Confirmez votre mot de passe")
22 class Meta(PersonneForm
.Meta
):
23 fields
= ('nom', 'prenom', 'courriel', 'password', 'password_confirmation', 'genre')
25 def clean_password(self
):
26 """Encrypter le mot de passe avant de le mettre dans la BD."""
27 return hashlib
.md5(self
.cleaned_data
['password']).hexdigest()
29 def clean_password_confirmation(self
):
30 """S'assurer que le mot de passe et la confirmation sont identiques."""
31 password
= self
.cleaned_data
['password']
32 confirmation
= hashlib
.md5(self
.cleaned_data
['password_confirmation']).hexdigest()
33 if password
!= confirmation
:
34 raise forms
.ValidationError('Les deux mots de passe ne correspondent pas.')
35 return self
.cleaned_data
['password_confirmation']
37 class ChercheurForm(forms
.ModelForm
):
38 """Formulaire d'édition d'un chercheur."""
39 ETABLISSEMENT_CHOICES
= ((id, nom
if len(nom
) < 80 else nom
[:80] + '...')
40 for id, nom
in Etablissement
.objects
.filter(membre
=True).values_list('id', 'nom'))
42 membre_instance_auf
= forms
.ChoiceField(
43 label
="Êtes-vous (ou avez-vous déjà été) membre d'une instance de l'AUF?",
44 help_text
="e.g. conseil scientifique, conseil associatif, commission régionale d'experts",
45 choices
=OUI_NON_CHOICES
, widget
=forms
.RadioSelect()
47 membre_instance_auf_details
= forms
.CharField(label
="Préciser laquelle et votre fonction", required
=False)
48 membre_instance_auf_dates
= forms
.CharField(label
="Préciser les dates", required
=False)
49 expert_oif
= forms
.ChoiceField(label
="Avez-vous déjà été sollicité par l'OIF?", choices
=OUI_NON_CHOICES
, widget
=forms
.RadioSelect())
50 expert_oif_details
= forms
.CharField(label
="Préciser à quel titre", required
=False,
51 help_text
="Fonction dans l'organisation, participation à une étude ou à une action, etc.")
52 expert_oif_dates
= forms
.CharField(label
="Préciser les dates", required
=False)
53 membre_association_francophone
= forms
.ChoiceField(
54 label
="Êtes-vous membre d'une association ou d'une société savante francophone?",
55 help_text
="e.g. FIPF, Collège international de philosophie, AISLF, etc.",
56 choices
=OUI_NON_CHOICES
, widget
=forms
.RadioSelect()
58 membre_association_francophone_details
= forms
.CharField(label
="Préciser laquelle", required
=False)
59 membre_reseau_institutionnel
= forms
.ChoiceField(
60 label
="Avez-vous fait partie des instances d'un réseau institutionnel de l'AUF?",
61 help_text
="e.g. AFELSH, RIFFEF, CIDMEF, etc.",
62 choices
=OUI_NON_CHOICES
, widget
=forms
.RadioSelect()
64 membre_reseau_institutionnel_details
= forms
.CharField(required
=False, label
="Préciser lesquelles et votre fonction")
65 membre_reseau_institutionnel_dates
= forms
.CharField(required
=False, label
="Préciser les dates")
67 etablissement
= forms
.ChoiceField(label
='Etablissement', required
=False, choices
=chain([('', '---------')], ETABLISSEMENT_CHOICES
))
71 fields
= ('statut', 'diplome', 'etablissement',
72 'etablissement_autre_nom', 'etablissement_autre_pays',
73 'discipline', 'theme_recherche', 'groupe_recherche', 'mots_cles',
74 'url_site_web', 'url_blog', 'url_reseau_social',
75 'membre_instance_auf', 'membre_instance_auf_details', 'membre_instance_auf_dates',
76 'expert_oif', 'expert_oif_details', 'expert_oif_dates',
77 'membre_association_francophone',
78 'membre_association_francophone_details',
79 'membre_reseau_institutionnel', 'membre_reseau_institutionnel_details',
80 'membre_reseau_institutionnel_dates')
82 def clean_membre_instance_auf(self
):
83 return bool(int(self
.cleaned_data
['membre_instance_auf']))
85 def clean_membre_instance_auf_details(self
):
86 membre
= self
.cleaned_data
.get('membre_instance_auf')
87 details
= self
.cleaned_data
.get('membre_instance_auf_details')
88 if membre
and not details
:
89 raise forms
.ValidationError('Veuillez préciser')
92 def clean_membre_instance_auf_dates(self
):
93 membre
= self
.cleaned_data
.get('membre_instance_auf')
94 dates
= self
.cleaned_data
.get('membre_instance_auf_dates')
95 if membre
and not dates
:
96 raise forms
.ValidationError('Veuillez préciser les dates')
99 def clean_expert_oif(self
):
100 return bool(int(self
.cleaned_data
['expert_oif']))
102 def clean_expert_oif_details(self
):
103 expert
= self
.cleaned_data
.get('expert_oif')
104 details
= self
.cleaned_data
.get('expert_oif_details')
105 if expert
and not details
:
106 raise forms
.ValidationError('Veuillez préciser')
109 def clean_expert_oif_dates(self
):
110 expert
= self
.cleaned_data
.get('expert_oif')
111 dates
= self
.cleaned_data
.get('expert_oif_dates')
112 if expert
and not dates
:
113 raise forms
.ValidationError('Veuillez préciser les dates')
116 def clean_membre_association_francophone(self
):
117 return bool(int(self
.cleaned_data
['membre_association_francophone']))
119 def clean_membre_association_francophone_details(self
):
120 membre
= self
.cleaned_data
.get('membre_association_francophone')
121 details
= self
.cleaned_data
.get('membre_association_francophone_details')
122 if membre
and not details
:
123 raise forms
.ValidationError('Veuillez préciser')
126 def clean_membre_reseau_institutionnel(self
):
127 return bool(int(self
.cleaned_data
['membre_reseau_institutionnel']))
129 def clean_membre_reseau_institutionnel_details(self
):
130 membre
= self
.cleaned_data
.get('membre_reseau_institutionnel')
131 details
= self
.cleaned_data
.get('membre_reseau_institutionnel_details')
132 if membre
and not details
:
133 raise forms
.ValidationError('Veuillez préciser')
136 def clean_membre_reseau_institutionnel_dates(self
):
137 membre
= self
.cleaned_data
.get('membre_reseau_institutionnel')
138 dates
= self
.cleaned_data
.get('membre_reseau_institutionnel_dates')
139 if membre
and not dates
:
140 raise forms
.ValidationError('Veuillez préciser les dates')
143 def clean_etablissement(self
):
144 etablissement
= self
.cleaned_data
['etablissement']
146 return Etablissement
.objects
.get(id=etablissement
)
149 etablissement
= self
.cleaned_data
['etablissement']
150 etablissement_autre_nom
= self
.cleaned_data
['etablissement_autre_nom']
151 etablissement_autre_pays
= self
.cleaned_data
['etablissement_autre_pays']
152 if not etablissement
:
153 if not etablissement_autre_nom
:
154 self
._errors
['etablissement'] = self
.error_class([u
"Vous devez renseigner l'établissement"])
155 elif not etablissement_autre_pays
:
156 self
._errors
['etablissement_autre_pays'] = self
.error_class([u
"Vous devez renseigner le pays de l'établissement"])
157 return self
.cleaned_data
159 class GroupesForm(forms
.Form
):
160 """Formulaire qui associe des groupes à un chercheur."""
161 groupes
= forms
.ModelMultipleChoiceField(
162 queryset
=Groupe
.objects
.all(),
163 label
='Domaines de recherche', required
=False,
164 help_text
="Maintenez appuyé « Ctrl », ou « Commande (touche pomme) » sur un Mac, pour en sélectionner plusieurs."
167 def __init__(self
, data
=None, prefix
=None, chercheur
=None):
168 self
.chercheur
= chercheur
171 initial
['groupes'] = chercheur
.groupes
.values_list('id', flat
=True)
172 super(GroupesForm
, self
).__init__(data
=data
, prefix
=prefix
, initial
=initial
)
176 groupes
= self
.cleaned_data
['groupes']
177 ChercheurGroupe
.objects
.filter(chercheur
=self
.chercheur
).exclude(groupe__in
=groupes
).delete()
179 ChercheurGroupe
.objects
.get_or_create(chercheur
=self
.chercheur
, groupe
=g
, actif
=1)
181 class PublicationForm(forms
.ModelForm
):
184 fields
= ('titre', 'revue', 'annee', 'editeur', 'lieu_edition', 'nb_pages', 'url')
186 class TheseForm(PublicationForm
):
187 titre
= forms
.CharField(required
=True, label
="Titre de la thèse ou du mémoire")
188 annee
= forms
.IntegerField(required
=True, label
="Année de soutenance (réalisée ou prévue)")
189 editeur
= forms
.CharField(required
=True, label
="Directeur de thèse ou de mémoire")
190 lieu_edition
= forms
.CharField(required
=True, label
="Établissement de soutenance")
193 fields
= ('titre', 'annee', 'editeur', 'lieu_edition', 'nb_pages', 'url')
195 class ExpertiseForm(forms
.ModelForm
):
196 organisme_demandeur_visible
= forms
.ChoiceField(
197 label
="Voulez-vous que l'organisme demandeur soit visible sur votre fiche?",
198 choices
=OUI_NON_CHOICES
, widget
=forms
.RadioSelect(), required
=False
202 fields
= ('nom', 'date', 'organisme_demandeur', 'organisme_demandeur_visible')
204 def clean_organisme_demandeur_visible(self
):
205 value
= self
.cleaned_data
['organisme_demandeur_visible']
206 return bool(int(value
)) if value
else False
208 ExpertiseFormSet
= inlineformset_factory(Chercheur
, Expertise
, form
=ExpertiseForm
, extra
=1)
210 class ChercheurFormGroup(object):
211 """Groupe de formulaires nécessaires pour l'inscription et l'édition
214 def __init__(self
, data
=None, chercheur
=None):
215 personne_form_class
= PersonneInscriptionForm
if chercheur
is None else PersonneForm
216 self
.chercheur
= ChercheurForm(data
=data
, prefix
='chercheur', instance
=chercheur
)
217 self
.groupes
= GroupesForm(data
=data
, prefix
='chercheur', chercheur
=chercheur
)
218 self
.personne
= personne_form_class(data
=data
, prefix
='personne', instance
=chercheur
and chercheur
.personne
)
219 self
.publication1
= PublicationForm(data
=data
, prefix
='publication1', instance
=chercheur
and chercheur
.publication1
)
220 self
.publication2
= PublicationForm(data
=data
, prefix
='publication2', instance
=chercheur
and chercheur
.publication2
)
221 self
.publication3
= PublicationForm(data
=data
, prefix
='publication3', instance
=chercheur
and chercheur
.publication3
)
222 self
.publication4
= PublicationForm(data
=data
, prefix
='publication4', instance
=chercheur
and chercheur
.publication4
)
223 self
.these
= TheseForm(data
=data
, prefix
='these', instance
=chercheur
and chercheur
.these
)
224 self
.expertises
= ExpertiseFormSet(data
=data
, prefix
='expertise', instance
=chercheur
)
227 def has_errors(self
):
228 return bool(self
.chercheur
.errors
or self
.personne
.errors
or self
.groupes
.errors
or
229 self
.publication1
.errors
or self
.publication2
.errors
or self
.publication3
.errors
or
230 self
.publication4
.errors
or self
.these
.errors
or self
.expertises
.errors
)
233 return self
.chercheur
.is_valid() and self
.personne
.is_valid() and self
.groupes
.is_valid() and \
234 self
.publication1
.is_valid() and self
.publication2
.is_valid() and \
235 self
.publication3
.is_valid() and self
.publication4
.is_valid() and \
236 self
.these
.is_valid() and self
.expertises
.is_valid()
241 chercheur
= self
.chercheur
.instance
243 # Enregistrer d'abord les clés étrangères car on doit les stocker dans
245 chercheur
.personne
= self
.personne
.save()
246 if self
.publication1
.cleaned_data
['titre']:
247 chercheur
.publication1
= self
.publication1
.save()
248 if self
.publication2
.cleaned_data
['titre']:
249 chercheur
.publication2
= self
.publication2
.save()
250 if self
.publication3
.cleaned_data
['titre']:
251 chercheur
.publication3
= self
.publication3
.save()
252 if self
.publication4
.cleaned_data
['titre']:
253 chercheur
.publication4
= self
.publication4
.save()
254 chercheur
.these
= self
.these
.save()
256 # Puis enregistrer le chercheur lui-même.
257 self
.chercheur
.save()
259 # Puis les many-to-many puisqu'on a besoin d'un id.
260 self
.groupes
.chercheur
= chercheur
262 self
.expertises
.instance
= chercheur
263 self
.expertises
.save()
265 class RepertoireSearchForm (forms
.Form
):
266 q
= forms
.CharField(required
=False, label
="Rechercher dans tous les champs")
267 nom
= forms
.CharField(required
=False, label
="Nom")
268 domaine
= forms
.ModelChoiceField(queryset
=Groupe
.objects
.all(), required
=False, label
="Domaine de recherche", empty_label
="Tous")
269 groupe_recherche
= forms
.CharField(required
=False, label
="Groupe de recherche",
270 help_text
="ou Laboratoire, ou Groupement inter-universitaire")
271 statut
= forms
.ChoiceField(choices
=(('','Tous'),)+STATUT_CHOICES
+(('expert','Expert'),), required
=False, label
="Statut")
272 discipline
= forms
.ModelChoiceField(queryset
=Discipline
.objects
.all(), required
=False, label
="Discipline", empty_label
="Toutes")
273 pays
= forms
.ModelChoiceField(queryset
=Pays
.objects
.all(), required
=False, label
="Pays", empty_label
="Tous")
274 region
= forms
.ModelChoiceField(queryset
=Region
.objects
.all(), required
=False, label
="Région", empty_label
="Toutes",
275 help_text
="La région est ici définie au sens, non strictement géographique, du Bureau régional de l'AUF de référence.")
276 nord_sud
= forms
.ChoiceField(choices
=(('', 'Tous'), ('Nord', 'Nord'), ('Sud', 'Sud')), required
=False, label
="Nord/Sud",
277 help_text
="Distinction d'ordre géopolitique et économique, non géographique, qui conditionne souvent l'attribution de soutiens par les agences internationales: on entend par Nord les pays les plus développés, par Sud les pays en voie de développement.")
279 def __init__(self
, data
=None, region
=None):
280 super(RepertoireSearchForm
, self
).__init__(data
)
282 pays
= self
.fields
['pays']
283 pays
.queryset
= pays
.queryset
.filter(region
=region
)
285 def get_query_set(self
):
286 qs
= Chercheur
.objects
.all()
288 nom
= self
.cleaned_data
['nom']
290 qs
= qs
.search_nom(nom
)
291 domaine
= self
.cleaned_data
["domaine"]
293 qs
= qs
.filter(groupes
=domaine
)
294 groupe_recherche
= self
.cleaned_data
['groupe_recherche']
296 for word
in groupe_recherche
.split():
297 qs
= qs
.filter(groupe_recherche__icontains
=word
)
298 q
= self
.cleaned_data
["q"]
301 statut
= self
.cleaned_data
["statut"]
303 if statut
== "expert":
304 qs
= qs
.exclude(expertises
=None)
306 qs
= qs
.filter(statut
=statut
)
307 discipline
= self
.cleaned_data
['discipline']
309 qs
= qs
.filter_discipline(discipline
)
310 region
= self
.cleaned_data
['region']
312 qs
= qs
.filter_region(region
)
313 pays
= self
.cleaned_data
["pays"]
315 qs
= qs
.filter(Q(etablissement__pays
=pays
) |
Q(etablissement_autre_pays
=pays
))
316 nord_sud
= self
.cleaned_data
['nord_sud']
318 qs
= qs
.filter(Q(etablissement__pays__nord_sud
=nord_sud
) |
Q(etablissement_autre_pays__nord_sud
=nord_sud
))
321 class SendPasswordForm(forms
.Form
):
322 email
= forms
.EmailField(required
=True, label
="Adresse électronique")
323 def clean_email(self
):
324 cleaned_data
= self
.cleaned_data
325 email
= cleaned_data
.get("email")
328 Utilisateur
.objects
.get(courriel
=email
)
330 raise forms
.ValidationError("Cette adresse n'existe pas dans notre base de données.")
333 class NewPasswordForm(forms
.Form
):
334 password
= forms
.CharField(widget
=forms
.PasswordInput(), required
=True, label
="Mot de passe")
335 password_repeat
= forms
.CharField(widget
=forms
.PasswordInput(), required
=True, label
="Confirmez mot de passe")
336 def clean_password_repeat(self
):
337 cleaned_data
= self
.cleaned_data
338 password
= cleaned_data
.get("password")
339 password_repeat
= cleaned_data
.get("password_repeat")
340 if password
and password_repeat
:
341 if password
!= password_repeat
:
342 raise forms
.ValidationError("Les mots de passe ne concordent pas")
343 return password_repeat