Ne pas limiter le login à 30 caractères
[auf_savoirs_en_partage_django.git] / auf_savoirs_en_partage / chercheurs / forms.py
1 # -*- encoding: utf-8 -*-
2 import hashlib
3 from django import forms
4 from django.contrib.auth.forms import AuthenticationForm as DjangoAuthenticationForm
5 from django.db.models import Q
6 from django.forms.models import inlineformset_factory
7 from itertools import chain
8 from models import *
9
10 OUI_NON_CHOICES = (('1', 'Oui'), ('0', 'Non'))
11
12 class ChercheurForm(forms.ModelForm):
13 """Formulaire d'édition d'un chercheur."""
14 genre = forms.ChoiceField(widget=forms.RadioSelect(), choices=GENRE_CHOICES)
15 afficher_courriel = forms.ChoiceField(
16 label="Afficher mon courriel publiquement sur ce site",
17 choices=OUI_NON_CHOICES, widget=forms.RadioSelect()
18 )
19 membre_instance_auf = forms.ChoiceField(
20 label="Êtes-vous (ou avez-vous déjà été) membre d'une instance de l'AUF?",
21 choices=OUI_NON_CHOICES, widget=forms.RadioSelect()
22 )
23 membre_instance_auf_nom = forms.ChoiceField(
24 choices = (('', '---------'),) + Chercheur.INSTANCE_AUF_CHOICES,
25 label="Préciser laquelle", required=False
26 )
27 membre_instance_auf_fonction = forms.CharField(label="Préciser votre fonction", required=False)
28 membre_instance_auf_dates = forms.CharField(label="Préciser les dates", required=False)
29 expert_oif = forms.ChoiceField(label="Avez-vous déjà été sollicité par l'OIF?", choices=OUI_NON_CHOICES, widget=forms.RadioSelect())
30 expert_oif_details = forms.CharField(label="Préciser à quel titre", required=False,
31 help_text="Fonction dans l'organisation, participation à une étude ou à une action, etc.")
32 expert_oif_dates = forms.CharField(label="Préciser les dates", required=False)
33 membre_association_francophone = forms.ChoiceField(
34 label="Êtes-vous membre d'une association ou d'une société savante francophone?",
35 help_text="e.g. FIPF, Collège international de philosophie, AISLF, etc.",
36 choices=OUI_NON_CHOICES, widget=forms.RadioSelect()
37 )
38 membre_association_francophone_details = forms.CharField(label="Préciser laquelle", required=False)
39 membre_reseau_institutionnel = forms.ChoiceField(
40 label="Êtes-vous (ou avez-vous déjà été) membre des instances d'un réseau institutionnel de l'AUF?",
41 choices=OUI_NON_CHOICES, widget=forms.RadioSelect()
42 )
43 membre_reseau_institutionnel_nom = forms.ChoiceField(
44 label="Préciser le réseau institutionnel",
45 choices=(('', '---------'),) + Chercheur.RESEAU_INSTITUTIONNEL_CHOICES,
46 required=False
47 )
48 membre_reseau_institutionnel_fonction = forms.CharField(required=False, label="Préciser votre fonction")
49 membre_reseau_institutionnel_dates = forms.CharField(required=False, label="Préciser les dates")
50
51 pays_etablissement = forms.ModelChoiceField(label="Pays de l'établissement", queryset=Pays.objects.all(), required=True)
52 etablissement = forms.CharField(
53 label="Nom de l'établissement", required=True,
54 help_text="Après avoir sélectionné un pays, une liste d'établissement apparaît dès la saisie partielle du nom de l'établissement."
55 )
56
57 pas_de_sollicitation_expertises = forms.BooleanField(
58 required=False,
59 label="Je ne souhaite pas être sollicité par l'AUF pour des missions d'expertise"
60 )
61
62 theme_recherche = forms.CharField(
63 max_length=1000, label='Thèmes de recherche', help_text='1000 signes maximum',
64 error_messages=dict(max_length="Veuillez entrer au maximum %(max)d signes (vous en avez entré %(length)d)."),
65 widget=forms.Textarea()
66 )
67 attestation = forms.BooleanField(
68 required=True,
69 label="J'atteste sur l'honneur l'exactitude des renseignements fournis sur le formulaire d'inscription et j'accepte leur publication en ligne."
70 )
71 discipline = forms.ModelChoiceField(
72 label="Discipline", required=True,
73 queryset=Discipline.objects.all(),
74 help_text="La liste des disciplines procède d'un choix fait par le conseil scientifique de l'AUF."
75 )
76 groupe_recherche = forms.CharField(
77 max_length=255, label='Groupe de recherche', required=False,
78 help_text="Indiquer l'appartenance à un groupe de recherche universitaire ou laboratoire ou groupement inter-universitaire"
79 )
80 url_site_web = forms.URLField(
81 label='Adresse site Internet', required=False,
82 help_text="Si vous le souhaitez, vous pouvez y indiquer le lien qui renvoie vers une page personnelle (sur le site de votre établissement par exemple) plus complète."
83 )
84
85 class Meta:
86 model = Chercheur
87 fields = ('nom', 'prenom', 'genre', 'afficher_courriel', 'adresse_postale', 'telephone',
88 'statut', 'diplome',
89 'discipline', 'theme_recherche', 'groupe_recherche',
90 'mots_cles', 'url_site_web', 'url_blog',
91 'url_reseau_social', 'attestation', 'membre_instance_auf',
92 'membre_instance_auf_nom', 'membre_instance_auf_fonction',
93 'membre_instance_auf_dates', 'expert_oif',
94 'expert_oif_details', 'expert_oif_dates',
95 'membre_association_francophone',
96 'membre_association_francophone_details',
97 'membre_reseau_institutionnel',
98 'membre_reseau_institutionnel_nom',
99 'membre_reseau_institutionnel_fonction',
100 'membre_reseau_institutionnel_dates')
101
102 def __init__(self, data=None, prefix=None, instance=None):
103 if instance is not None:
104 initial = {}
105 if instance.etablissement:
106 initial['etablissement'] = instance.etablissement.nom
107 initial['pays_etablissement'] = instance.etablissement.pays_id
108 else:
109 initial['etablissement'] = instance.etablissement_autre_nom
110 initial['pays_etablissement'] = instance.etablissement_autre_pays_id
111 initial['pas_de_sollicitation_expertises'] = not instance.expertises_auf
112 else:
113 initial = None
114 super(ChercheurForm, self).__init__(data=data, prefix=prefix, instance=instance, initial=initial)
115
116 def save(self):
117 nom_etablissement = self.cleaned_data['etablissement']
118 pays_etablissement = self.cleaned_data['pays_etablissement']
119 etablissements = Etablissement.objects.filter(nom=nom_etablissement, pays=pays_etablissement, actif=True)
120 if etablissements.count() > 0:
121 self.instance.etablissement = etablissements[0]
122 self.instance.etablissement_autre = ''
123 self.instance.etablissement_autre_pays = None
124 else:
125 self.instance.etablissement = None
126 self.instance.etablissement_autre_nom = nom_etablissement
127 self.instance.etablissement_autre_pays = pays_etablissement
128 self.instance.expertises_auf = not self.cleaned_data['pas_de_sollicitation_expertises']
129 super(ChercheurForm, self).save()
130
131 def clean_courriel(self):
132 """On veut s'assurer qu'il n'y ait pas d'autre utilisateur actif
133 avec le même courriel."""
134 courriel = self.cleaned_data['courriel']
135 existing = Chercheur.objects.filter(courriel=courriel, actif=True)
136 if self.instance and self.instance.id:
137 existing = existing.exclude(id=self.instance.id)
138 if existing.count():
139 raise forms.ValidationError('Il existe déjà une fiche pour cette adresse électronique')
140 return courriel
141
142 def clean_afficher_courriel(self):
143 return bool(int(self.cleaned_data['afficher_courriel']))
144
145 def clean_membre_instance_auf(self):
146 return bool(int(self.cleaned_data['membre_instance_auf']))
147
148 def clean_membre_instance_auf_nom(self):
149 membre = self.cleaned_data.get('membre_instance_auf')
150 nom = self.cleaned_data.get('membre_instance_auf_nom')
151 if membre and not nom:
152 raise forms.ValidationError('Veuillez préciser')
153 return nom
154
155 def clean_membre_instance_auf_fonction(self):
156 membre = self.cleaned_data.get('membre_instance_auf')
157 fonction = self.cleaned_data.get('membre_instance_auf_fonction')
158 if membre and not fonction:
159 raise forms.ValidationError('Veuillez préciser')
160 return fonction
161
162 def clean_membre_instance_auf_dates(self):
163 membre = self.cleaned_data.get('membre_instance_auf')
164 dates = self.cleaned_data.get('membre_instance_auf_dates')
165 if membre and not dates:
166 raise forms.ValidationError('Veuillez préciser les dates')
167 return dates
168
169 def clean_expert_oif(self):
170 return bool(int(self.cleaned_data['expert_oif']))
171
172 def clean_expert_oif_details(self):
173 expert = self.cleaned_data.get('expert_oif')
174 details = self.cleaned_data.get('expert_oif_details')
175 if expert and not details:
176 raise forms.ValidationError('Veuillez préciser')
177 return details
178
179 def clean_expert_oif_dates(self):
180 expert = self.cleaned_data.get('expert_oif')
181 dates = self.cleaned_data.get('expert_oif_dates')
182 if expert and not dates:
183 raise forms.ValidationError('Veuillez préciser les dates')
184 return dates
185
186 def clean_membre_association_francophone(self):
187 return bool(int(self.cleaned_data['membre_association_francophone']))
188
189 def clean_membre_association_francophone_details(self):
190 membre = self.cleaned_data.get('membre_association_francophone')
191 details = self.cleaned_data.get('membre_association_francophone_details')
192 if membre and not details:
193 raise forms.ValidationError('Veuillez préciser')
194 return details
195
196 def clean_membre_reseau_institutionnel(self):
197 return bool(int(self.cleaned_data['membre_reseau_institutionnel']))
198
199 def clean_membre_reseau_institutionnel_nom(self):
200 membre = self.cleaned_data.get('membre_reseau_institutionnel')
201 nom = self.cleaned_data.get('membre_reseau_institutionnel_nom')
202 if membre and not nom:
203 raise forms.ValidationError('Veuillez préciser')
204 return nom
205
206 def clean_membre_reseau_institutionnel_fonction(self):
207 membre = self.cleaned_data.get('membre_reseau_institutionnel')
208 fonction = self.cleaned_data.get('membre_reseau_institutionnel_fonction')
209 if membre and not fonction:
210 raise forms.ValidationError('Veuillez préciser')
211 return fonction
212
213 def clean_membre_reseau_institutionnel_dates(self):
214 membre = self.cleaned_data.get('membre_reseau_institutionnel')
215 dates = self.cleaned_data.get('membre_reseau_institutionnel_dates')
216 if membre and not dates:
217 raise forms.ValidationError('Veuillez préciser les dates')
218 return dates
219
220 class ChercheurInscriptionForm(ChercheurForm):
221
222 class Meta(ChercheurForm.Meta):
223 fields = ChercheurForm.Meta.fields + ('courriel',)
224
225 class GroupesForm(forms.Form):
226 """Formulaire qui associe des groupes à un chercheur."""
227 groupes = forms.ModelMultipleChoiceField(
228 queryset=Groupe.objects.all(),
229 label='Domaines de recherche', required=False,
230 help_text="Ce champ est proposé à titre d'indication complémentaire, mais il n'est pas obligatoire. Maintenez appuyé « Ctrl », ou « Commande (touche pomme) » sur un Mac, pour en sélectionner plusieurs."
231 )
232
233 def __init__(self, data=None, prefix=None, chercheur=None):
234 self.chercheur = chercheur
235 initial = {}
236 if chercheur:
237 initial['groupes'] = chercheur.groupes.values_list('id', flat=True)
238 super(GroupesForm, self).__init__(data=data, prefix=prefix, initial=initial)
239
240 def save(self):
241 if self.is_valid():
242 groupes = self.cleaned_data['groupes']
243 ChercheurGroupe.objects.filter(chercheur=self.chercheur).exclude(groupe__in=groupes).delete()
244 for g in groupes:
245 ChercheurGroupe.objects.get_or_create(chercheur=self.chercheur, groupe=g, actif=1)
246
247 class PublicationForm(forms.ModelForm):
248 class Meta:
249 model = Publication
250 fields = ('auteurs', 'titre', 'revue', 'annee', 'editeur', 'lieu_edition', 'nb_pages', 'url')
251
252 PublicationFormSet = inlineformset_factory(Chercheur, Publication, form=PublicationForm, extra=1)
253
254 class TheseForm(forms.ModelForm):
255 class Meta:
256 model = These
257 fields = ('titre', 'annee', 'directeur', 'etablissement', 'nb_pages', 'url')
258
259 class ExpertiseForm(forms.ModelForm):
260 organisme_demandeur_visible = forms.ChoiceField(
261 label="Voulez-vous que l'organisme demandeur soit visible sur votre fiche?",
262 choices=OUI_NON_CHOICES, widget=forms.RadioSelect(), required=False
263 )
264 class Meta:
265 model = Expertise
266 fields = ('nom', 'date', 'organisme_demandeur', 'organisme_demandeur_visible')
267
268 def clean_organisme_demandeur_visible(self):
269 value = self.cleaned_data['organisme_demandeur_visible']
270 return bool(int(value)) if value else False
271
272 ExpertiseFormSet = inlineformset_factory(Chercheur, Expertise, form=ExpertiseForm, extra=1)
273
274 class ChercheurFormGroup(object):
275 """Groupe de formulaires nécessaires pour l'inscription et l'édition
276 d'un chercheur."""
277
278 def __init__(self, data=None, chercheur=None):
279 try:
280 these = chercheur and chercheur.these
281 except These.DoesNotExist:
282 these = These()
283 chercheur_form_class = ChercheurInscriptionForm if chercheur is None else ChercheurForm
284 self.chercheur = chercheur_form_class(data=data, prefix='chercheur', instance=chercheur)
285 self.groupes = GroupesForm(data=data, prefix='chercheur', chercheur=chercheur)
286 self.expertises = ExpertiseFormSet(data=data, prefix='expertise', instance=chercheur)
287 self.these = TheseForm(data=data, prefix='these', instance=these)
288 self.publications = PublicationFormSet(data=data, prefix='publication', instance=chercheur)
289
290 @property
291 def has_errors(self):
292 return bool(self.chercheur.errors or self.groupes.errors or
293 self.these.errors or self.publications.errors or
294 self.expertises.errors)
295
296 def is_valid(self):
297 return self.chercheur.is_valid() and self.groupes.is_valid() and \
298 self.these.is_valid() and self.publications.is_valid() and \
299 self.expertises.is_valid()
300
301 def save(self):
302 if self.is_valid():
303
304 # Enregistrer d'abord le chercheur lui-même.
305 self.chercheur.save()
306
307 # Puis les objets qui ont des clés étrangères vers nous
308 # puisqu'on a besoin d'un id.
309 chercheur = self.chercheur.instance
310 self.groupes.chercheur = chercheur
311 self.groupes.save()
312 self.these.instance.chercheur = chercheur
313 self.these.save()
314 self.publications.instance = chercheur
315 self.publications.save()
316 self.expertises.instance = chercheur
317 self.expertises.save()
318 return self.chercheur.instance
319
320 class RepertoireSearchForm (forms.Form):
321 q = forms.CharField(required=False, label="Rechercher dans tous les champs")
322 nom = forms.CharField(required=False, label="Nom")
323 domaine = forms.ModelChoiceField(queryset=Groupe.objects.all(), required=False, label="Domaine de recherche", empty_label="Tous")
324 groupe_recherche = forms.CharField(required=False, label="Groupe de recherche",
325 help_text="ou Laboratoire, ou Groupement inter-universitaire")
326 statut = forms.ChoiceField(choices=(('','Tous'),)+STATUT_CHOICES+(('expert','Expert'),), required=False, label="Statut")
327 discipline = forms.ModelChoiceField(queryset=Discipline.objects.all(), required=False, label="Discipline", empty_label="Toutes")
328 pays = forms.ModelChoiceField(queryset=Pays.objects.all(), required=False, label="Pays", empty_label="Tous")
329 region = forms.ModelChoiceField(queryset=Region.objects.all(), required=False, label="Région", empty_label="Toutes",
330 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.")
331 nord_sud = forms.ChoiceField(choices=(('', 'Tous'), ('Nord', 'Nord'), ('Sud', 'Sud')), required=False, label="Nord/Sud",
332 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 développés, par Sud les pays en développement (pays les moins avancés, pays émergents et pays à économies en transition)")
333 activites_francophonie = forms.ChoiceField(required=False, label="Activités en Francophonie", choices=(
334 ('', '---------'),
335 ('instance_auf', "Membre d'une instance de l'AUF"),
336 ('expert_oif', "Sollicité par l'OIF"),
337 ('association_francophone', "Membre d'une association ou d'une société savante francophone"),
338 ('reseau_institutionnel', "Membre des instances d'un réseau institutionnel de l'AUF")
339 ))
340 genre = forms.ChoiceField(choices=((('', 'Tous'),) + GENRE_CHOICES), required=False, label="Genre")
341
342 def __init__(self, data=None, region=None):
343 super(RepertoireSearchForm, self).__init__(data)
344 if region:
345 pays = self.fields['pays']
346 pays.queryset = pays.queryset.filter(region=region)
347
348 def get_query_set(self):
349 chercheurs = Chercheur.objects
350 if self.is_valid():
351 q = self.cleaned_data["q"]
352 if q:
353 chercheurs = chercheurs.search(q)
354 nom = self.cleaned_data['nom']
355 if nom:
356 chercheurs = chercheurs.add_to_query('@(nom,prenom) ' + nom)
357 groupe_recherche = self.cleaned_data['groupe_recherche']
358 if groupe_recherche:
359 chercheurs = chercheurs.add_to_query('@groupe_recherche ' + groupe_recherche)
360 discipline = self.cleaned_data['discipline']
361 if discipline:
362 chercheurs = chercheurs.filter_discipline(discipline)
363 region = self.cleaned_data['region']
364 if region:
365 chercheurs = chercheurs.filter_region(region)
366 statut = self.cleaned_data["statut"]
367 if statut:
368 if statut == "expert":
369 chercheurs = chercheurs.filter_expert()
370 else:
371 chercheurs = chercheurs.filter_statut(statut)
372 domaine = self.cleaned_data["domaine"]
373 if domaine:
374 chercheurs = chercheurs.filter_groupe(domaine)
375 pays = self.cleaned_data["pays"]
376 if pays:
377 chercheurs = chercheurs.filter_pays(pays)
378 nord_sud = self.cleaned_data['nord_sud']
379 if nord_sud:
380 chercheurs = chercheurs.filter_nord_sud(nord_sud)
381 genre = self.cleaned_data['genre']
382 if genre:
383 chercheurs = chercheurs.filter_genre(genre)
384 activites_francophonie = self.cleaned_data['activites_francophonie']
385 if activites_francophonie == 'instance_auf':
386 chercheurs = chercheurs.filter(membre_instance_auf=True)
387 elif activites_francophonie == 'expert_oif':
388 chercheurs = chercheurs.filter(expert_oif=True)
389 elif activites_francophonie == 'association_francophone':
390 chercheurs = chercheurs.filter(membre_association_francophone=True)
391 elif activites_francophonie == 'reseau_institutionnel':
392 chercheurs = chercheurs.filter(membre_reseau_institutionnel=True)
393 return chercheurs.all()
394
395 class SendPasswordForm(forms.Form):
396 email = forms.EmailField(required=True, label="Adresse électronique")
397 def clean_email(self):
398 cleaned_data = self.cleaned_data
399 email = cleaned_data.get("email")
400 if email:
401 try:
402 Personne.objects.get(courriel=email)
403 except:
404 raise forms.ValidationError("Cette adresse n'existe pas dans notre base de données.")
405 return email
406
407 class SetPasswordForm(forms.Form):
408 password = forms.CharField(widget=forms.PasswordInput(), required=True, label="Mot de passe")
409 password_repeat = forms.CharField(widget=forms.PasswordInput(), required=True, label="Confirmez votre mot de passe")
410
411 def clean_password_repeat(self):
412 cleaned_data = self.cleaned_data
413 password = cleaned_data.get("password")
414 password_repeat = cleaned_data.get("password_repeat")
415 if password and password_repeat:
416 if password != password_repeat:
417 raise forms.ValidationError("Les mots de passe ne concordent pas")
418 return password_repeat
419
420 class AuthenticationForm(DjangoAuthenticationForm):
421 username = forms.CharField(label='Courriel')