Limiter les thèmes de recherche à 1000 signes
[auf_savoirs_en_partage_django.git] / auf_savoirs_en_partage / chercheurs / forms.py
CommitLineData
932eef9a 1# -*- encoding: utf-8 -*-
a7b16ec9 2import hashlib
932eef9a 3from django import forms
3efbacbe 4from django.db.models import Q
ee8b3a49 5from django.forms.models import inlineformset_factory
fbcbdde0 6from itertools import chain
932eef9a 7from models import *
13146d99 8
c073c94d
EMS
9OUI_NON_CHOICES = (('1', 'Oui'), ('0', 'Non'))
10
932eef9a 11class PersonneForm(forms.ModelForm):
c18af6bd 12 genre = forms.ChoiceField(widget=forms.RadioSelect(), choices=GENRE_CHOICES)
e4d01d1d 13
932eef9a 14 class Meta:
dab519fa 15 model = Utilisateur
e4d01d1d
EMS
16 fields = ('nom', 'prenom', 'courriel', 'genre')
17
595ab4d6
EMS
18 def clean_courriel(self):
19 """On veut s'assurer qu'il n'y ait pas d'autre utilisateur actif
20 avec le même courriel."""
21 courriel = self.cleaned_data['courriel']
27775adc
EMS
22 existing = Personne.objects.filter(courriel=courriel, actif=True)
23 if self.instance and self.instance.id:
24 existing = existing.exclude(id=self.instance.id)
25 if existing.count():
595ab4d6
EMS
26 raise forms.ValidationError('Il existe déjà une fiche pour cette adresse électronique')
27 return courriel
28
e4d01d1d
EMS
29class PersonneInscriptionForm(PersonneForm):
30 password = forms.CharField(widget=forms.PasswordInput(), label="Mot de passe")
ea51135b 31 password_confirmation = forms.CharField(widget=forms.PasswordInput(), label="Confirmez votre mot de passe")
e4d01d1d
EMS
32
33 class Meta(PersonneForm.Meta):
ea51135b 34 fields = ('nom', 'prenom', 'courriel', 'password', 'password_confirmation', 'genre')
5ecd9e43 35
ea51135b
EMS
36 def clean_password_confirmation(self):
37 """S'assurer que le mot de passe et la confirmation sont identiques."""
fd559765 38 password = self.cleaned_data.get('password')
92990258 39 confirmation = self.cleaned_data.get('password_confirmation')
ea51135b
EMS
40 if password != confirmation:
41 raise forms.ValidationError('Les deux mots de passe ne correspondent pas.')
92990258
EMS
42 return confirmation
43
44 def save(self):
45 self.instance.set_password(self.cleaned_data['password'])
46 return super(PersonneInscriptionForm, self).save()
ea51135b 47
932eef9a 48class ChercheurForm(forms.ModelForm):
a7b16ec9 49 """Formulaire d'édition d'un chercheur."""
1de0a686 50 ETABLISSEMENT_CHOICES = ((id, nom if len(nom) < 80 else nom[:80] + '...')
d84e8b50 51 for id, nom in Etablissement.objects.filter(membre=True).values_list('id', 'nom'))
a7b16ec9
EMS
52
53 membre_instance_auf = forms.ChoiceField(
a7b16ec9
EMS
54 label="Êtes-vous (ou avez-vous déjà été) membre d'une instance de l'AUF?",
55 help_text="e.g. conseil scientifique, conseil associatif, commission régionale d'experts",
c073c94d 56 choices=OUI_NON_CHOICES, widget=forms.RadioSelect()
a7b16ec9 57 )
614b3269 58 membre_instance_auf_details = forms.CharField(label="Préciser laquelle et votre fonction", required=False)
a7b16ec9 59 membre_instance_auf_dates = forms.CharField(label="Préciser les dates", required=False)
614b3269
EMS
60 expert_oif = forms.ChoiceField(label="Avez-vous déjà été sollicité par l'OIF?", choices=OUI_NON_CHOICES, widget=forms.RadioSelect())
61 expert_oif_details = forms.CharField(label="Préciser à quel titre", required=False,
62 help_text="Fonction dans l'organisation, participation à une étude ou à une action, etc.")
63 expert_oif_dates = forms.CharField(label="Préciser les dates", required=False)
c073c94d
EMS
64 membre_association_francophone = forms.ChoiceField(
65 label="Êtes-vous membre d'une association ou d'une société savante francophone?",
66 help_text="e.g. FIPF, Collège international de philosophie, AISLF, etc.",
67 choices=OUI_NON_CHOICES, widget=forms.RadioSelect()
68 )
69 membre_association_francophone_details = forms.CharField(label="Préciser laquelle", required=False)
70 membre_reseau_institutionnel = forms.ChoiceField(
71 label="Avez-vous fait partie des instances d'un réseau institutionnel de l'AUF?",
72 help_text="e.g. AFELSH, RIFFEF, CIDMEF, etc.",
73 choices=OUI_NON_CHOICES, widget=forms.RadioSelect()
74 )
75 membre_reseau_institutionnel_details = forms.CharField(required=False, label="Préciser lesquelles et votre fonction")
76 membre_reseau_institutionnel_dates = forms.CharField(required=False, label="Préciser les dates")
77
fbcbdde0 78 etablissement = forms.ChoiceField(label='Etablissement', required=False, choices=chain([('', '---------')], ETABLISSEMENT_CHOICES))
a7b16ec9 79
cb591fb3
EMS
80 expertises_auf = forms.ChoiceField(
81 label="Êtes-vous disposé à réaliser des expertises pour l'AUF?",
82 choices=OUI_NON_CHOICES, widget=forms.RadioSelect()
83 )
84
332975c3
EMS
85 theme_recherche = forms.CharField(
86 max_length=1000, label='Thèmes de recherche', help_text='1000 signes maximum',
87 error_messages=dict(max_length="Veuillez entrer au maximum %(max)d signes (vous en avez entré %(length)d)."),
88 widget=forms.Textarea()
89 )
90
932eef9a
AJ
91 class Meta:
92 model = Chercheur
a7b16ec9
EMS
93 fields = ('statut', 'diplome', 'etablissement',
94 'etablissement_autre_nom', 'etablissement_autre_pays',
0b0fbbd7 95 'discipline', 'theme_recherche', 'groupe_recherche', 'mots_cles',
a7b16ec9 96 'url_site_web', 'url_blog', 'url_reseau_social',
614b3269
EMS
97 'membre_instance_auf', 'membre_instance_auf_details', 'membre_instance_auf_dates',
98 'expert_oif', 'expert_oif_details', 'expert_oif_dates',
99 'membre_association_francophone',
100 'membre_association_francophone_details',
c073c94d 101 'membre_reseau_institutionnel', 'membre_reseau_institutionnel_details',
cb591fb3 102 'membre_reseau_institutionnel_dates', 'expertises_auf')
7c596de2 103
c073c94d
EMS
104 def clean_membre_instance_auf(self):
105 return bool(int(self.cleaned_data['membre_instance_auf']))
106
614b3269
EMS
107 def clean_membre_instance_auf_details(self):
108 membre = self.cleaned_data.get('membre_instance_auf')
109 details = self.cleaned_data.get('membre_instance_auf_details')
110 if membre and not details:
111 raise forms.ValidationError('Veuillez préciser')
112 return details
113
c073c94d
EMS
114 def clean_membre_instance_auf_dates(self):
115 membre = self.cleaned_data.get('membre_instance_auf')
116 dates = self.cleaned_data.get('membre_instance_auf_dates')
117 if membre and not dates:
118 raise forms.ValidationError('Veuillez préciser les dates')
119 return dates
120
121 def clean_expert_oif(self):
122 return bool(int(self.cleaned_data['expert_oif']))
123
614b3269
EMS
124 def clean_expert_oif_details(self):
125 expert = self.cleaned_data.get('expert_oif')
126 details = self.cleaned_data.get('expert_oif_details')
127 if expert and not details:
128 raise forms.ValidationError('Veuillez préciser')
129 return details
130
131 def clean_expert_oif_dates(self):
132 expert = self.cleaned_data.get('expert_oif')
133 dates = self.cleaned_data.get('expert_oif_dates')
134 if expert and not dates:
135 raise forms.ValidationError('Veuillez préciser les dates')
136 return dates
137
c073c94d
EMS
138 def clean_membre_association_francophone(self):
139 return bool(int(self.cleaned_data['membre_association_francophone']))
140
141 def clean_membre_association_francophone_details(self):
142 membre = self.cleaned_data.get('membre_association_francophone')
143 details = self.cleaned_data.get('membre_association_francophone_details')
144 if membre and not details:
145 raise forms.ValidationError('Veuillez préciser')
146 return details
147
148 def clean_membre_reseau_institutionnel(self):
149 return bool(int(self.cleaned_data['membre_reseau_institutionnel']))
150
151 def clean_membre_reseau_institutionnel_details(self):
152 membre = self.cleaned_data.get('membre_reseau_institutionnel')
153 details = self.cleaned_data.get('membre_reseau_institutionnel_details')
154 if membre and not details:
155 raise forms.ValidationError('Veuillez préciser')
156 return details
157
158 def clean_membre_reseau_institutionnel_dates(self):
159 membre = self.cleaned_data.get('membre_reseau_institutionnel')
160 dates = self.cleaned_data.get('membre_reseau_institutionnel_dates')
161 if membre and not dates:
162 raise forms.ValidationError('Veuillez préciser les dates')
163 return dates
164
1de0a686 165 def clean_etablissement(self):
fbcbdde0
EMS
166 etablissement = self.cleaned_data['etablissement']
167 if etablissement:
168 return Etablissement.objects.get(id=etablissement)
1de0a686 169
cb591fb3
EMS
170 def clean_expertises_auf(self):
171 return bool(int(self.cleaned_data['expertises_auf']))
172
a7b16ec9
EMS
173 def clean(self):
174 etablissement = self.cleaned_data['etablissement']
175 etablissement_autre_nom = self.cleaned_data['etablissement_autre_nom']
176 etablissement_autre_pays = self.cleaned_data['etablissement_autre_pays']
177 if not etablissement:
178 if not etablissement_autre_nom:
179 self._errors['etablissement'] = self.error_class([u"Vous devez renseigner l'établissement"])
180 elif not etablissement_autre_pays:
181 self._errors['etablissement_autre_pays'] = self.error_class([u"Vous devez renseigner le pays de l'établissement"])
182 return self.cleaned_data
183
d160d3fa
EMS
184class ChercheurInscriptionForm(ChercheurForm):
185 attestation = forms.BooleanField(
186 required=True,
187 label="J'atteste sur l'honneur l'exactitude des renseignements fournis sur le formulaire d'inscription et j'accepte leur publication en ligne."
188 )
189
a7b16ec9
EMS
190class GroupesForm(forms.Form):
191 """Formulaire qui associe des groupes à un chercheur."""
e4d01d1d
EMS
192 groupes = forms.ModelMultipleChoiceField(
193 queryset=Groupe.objects.all(),
194 label='Domaines de recherche', required=False,
195 help_text="Maintenez appuyé « Ctrl », ou « Commande (touche pomme) » sur un Mac, pour en sélectionner plusieurs."
196 )
a7b16ec9
EMS
197
198 def __init__(self, data=None, prefix=None, chercheur=None):
199 self.chercheur = chercheur
200 initial = {}
201 if chercheur:
202 initial['groupes'] = chercheur.groupes.values_list('id', flat=True)
203 super(GroupesForm, self).__init__(data=data, prefix=prefix, initial=initial)
204
205 def save(self):
206 if self.is_valid():
207 groupes = self.cleaned_data['groupes']
208 ChercheurGroupe.objects.filter(chercheur=self.chercheur).exclude(groupe__in=groupes).delete()
209 for g in groupes:
210 ChercheurGroupe.objects.get_or_create(chercheur=self.chercheur, groupe=g, actif=1)
211
00755d9b
AJ
212class PublicationForm(forms.ModelForm):
213 class Meta:
214 model = Publication
1df3737b 215 fields = ('auteurs', 'titre', 'revue', 'annee', 'editeur', 'lieu_edition', 'nb_pages', 'url', 'publication_affichage')
f810842d 216
595ab4d6
EMS
217PublicationFormSet = inlineformset_factory(Chercheur, Publication, form=PublicationForm, extra=1)
218
219class TheseForm(forms.ModelForm):
e8e9e4fd 220 class Meta:
595ab4d6
EMS
221 model = These
222 fields = ('titre', 'annee', 'directeur', 'etablissement', 'nb_pages', 'url')
2a36714f 223
5b9abc81 224class ExpertiseForm(forms.ModelForm):
c073c94d
EMS
225 organisme_demandeur_visible = forms.ChoiceField(
226 label="Voulez-vous que l'organisme demandeur soit visible sur votre fiche?",
227 choices=OUI_NON_CHOICES, widget=forms.RadioSelect(), required=False
228 )
5b9abc81
AJ
229 class Meta:
230 model = Expertise
231 fields = ('nom', 'date', 'organisme_demandeur', 'organisme_demandeur_visible')
dab519fa 232
b16bcbaf 233 def clean_organisme_demandeur_visible(self):
6504a7e5
EMS
234 value = self.cleaned_data['organisme_demandeur_visible']
235 return bool(int(value)) if value else False
b16bcbaf 236
ee8b3a49
EMS
237ExpertiseFormSet = inlineformset_factory(Chercheur, Expertise, form=ExpertiseForm, extra=1)
238
a7b16ec9
EMS
239class ChercheurFormGroup(object):
240 """Groupe de formulaires nécessaires pour l'inscription et l'édition
241 d'un chercheur."""
00755d9b 242
a7b16ec9 243 def __init__(self, data=None, chercheur=None):
e4d01d1d 244 personne_form_class = PersonneInscriptionForm if chercheur is None else PersonneForm
d160d3fa
EMS
245 chercheur_form_class = ChercheurInscriptionForm if chercheur is None else ChercheurForm
246 self.chercheur = chercheur_form_class(data=data, prefix='chercheur', instance=chercheur)
a7b16ec9 247 self.groupes = GroupesForm(data=data, prefix='chercheur', chercheur=chercheur)
92990258 248 self.personne = personne_form_class(data=data, prefix='personne', instance=chercheur and chercheur.personne.utilisateur)
ee8b3a49 249 self.expertises = ExpertiseFormSet(data=data, prefix='expertise', instance=chercheur)
595ab4d6
EMS
250 self.these = TheseForm(data=data, prefix='these', instance=chercheur and chercheur.these)
251 self.publications = PublicationFormSet(data=data, prefix='publication', instance=chercheur)
a7b16ec9
EMS
252
253 @property
254 def has_errors(self):
255 return bool(self.chercheur.errors or self.personne.errors or self.groupes.errors or
595ab4d6 256 self.these.errors or self.publications.errors or self.expertises.errors)
a7b16ec9
EMS
257
258 def is_valid(self):
259 return self.chercheur.is_valid() and self.personne.is_valid() and self.groupes.is_valid() and \
595ab4d6 260 self.these.is_valid() and self.publications.is_valid() and self.expertises.is_valid()
a7b16ec9
EMS
261
262 def save(self):
263 if self.is_valid():
264
265 chercheur = self.chercheur.instance
266
267 # Enregistrer d'abord les clés étrangères car on doit les stocker dans
268 # l'objet chercheur.
269 chercheur.personne = self.personne.save()
a7b16ec9
EMS
270
271 # Puis enregistrer le chercheur lui-même.
272 self.chercheur.save()
273
595ab4d6
EMS
274 # Puis les objets qui ont des clés étrangères vers nous
275 # puisqu'on a besoin d'un id.
a7b16ec9
EMS
276 self.groupes.chercheur = chercheur
277 self.groupes.save()
595ab4d6
EMS
278 self.these.instance.chercheur = chercheur
279 self.these.save()
280 self.publications.instance = chercheur
281 self.publications.save()
3820b46e
EMS
282 self.expertises.instance = chercheur
283 self.expertises.save()
5b9abc81 284
13146d99 285class RepertoireSearchForm (forms.Form):
f0692c02 286 q = forms.CharField(required=False, label="Rechercher dans tous les champs")
3efbacbe 287 nom = forms.CharField(required=False, label="Nom")
0e9597af 288 domaine = forms.ModelChoiceField(queryset=Groupe.objects.all(), required=False, label="Domaine de recherche", empty_label="Tous")
bc415771
EMS
289 groupe_recherche = forms.CharField(required=False, label="Groupe de recherche",
290 help_text="ou Laboratoire, ou Groupement inter-universitaire")
6bd49ff1 291 statut = forms.ChoiceField(choices=(('','Tous'),)+STATUT_CHOICES+(('expert','Expert'),), required=False, label="Statut")
f0692c02 292 discipline = forms.ModelChoiceField(queryset=Discipline.objects.all(), required=False, label="Discipline", empty_label="Toutes")
3eba0476 293 pays = forms.ModelChoiceField(queryset=Pays.objects.all(), required=False, label="Pays", empty_label="Tous")
bc415771
EMS
294 region = forms.ModelChoiceField(queryset=Region.objects.all(), required=False, label="Région", empty_label="Toutes",
295 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.")
bc415771 296 nord_sud = forms.ChoiceField(choices=(('', 'Tous'), ('Nord', 'Nord'), ('Sud', 'Sud')), required=False, label="Nord/Sud",
5eecdf3e 297 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)")
c548d94a
EMS
298
299 def __init__(self, data=None, region=None):
300 super(RepertoireSearchForm, self).__init__(data)
301 if region:
302 pays = self.fields['pays']
303 pays.queryset = pays.queryset.filter(region=region)
304
3efbacbe 305 def get_query_set(self):
116db1fd 306 qs = Chercheur.objects.all()
3efbacbe
EMS
307 if self.is_valid():
308 nom = self.cleaned_data['nom']
309 if nom:
116db1fd
EMS
310 qs = qs.search_nom(nom)
311 domaine = self.cleaned_data["domaine"]
312 if domaine:
313 qs = qs.filter(groupes=domaine)
d087521a
EMS
314 groupe_recherche = self.cleaned_data['groupe_recherche']
315 if groupe_recherche:
116db1fd
EMS
316 for word in groupe_recherche.split():
317 qs = qs.filter(groupe_recherche__icontains=word)
318 q = self.cleaned_data["q"]
319 if q:
320 qs = qs.search(q)
5212238e
EMS
321 statut = self.cleaned_data["statut"]
322 if statut:
323 if statut == "expert":
116db1fd 324 qs = qs.exclude(expertises=None)
5212238e 325 else:
116db1fd
EMS
326 qs = qs.filter(statut=statut)
327 discipline = self.cleaned_data['discipline']
328 if discipline:
329 qs = qs.filter_discipline(discipline)
330 region = self.cleaned_data['region']
331 if region:
332 qs = qs.filter_region(region)
62eece34
EMS
333 pays = self.cleaned_data["pays"]
334 if pays:
116db1fd 335 qs = qs.filter(Q(etablissement__pays=pays) | Q(etablissement_autre_pays=pays))
62eece34
EMS
336 nord_sud = self.cleaned_data['nord_sud']
337 if nord_sud:
116db1fd
EMS
338 qs = qs.filter(Q(etablissement__pays__nord_sud=nord_sud) | Q(etablissement_autre_pays__nord_sud=nord_sud))
339 return qs
3efbacbe 340
0e9597af 341class SendPasswordForm(forms.Form):
89da853e 342 email = forms.EmailField(required=True, label="Adresse électronique")
0e9597af
AJ
343 def clean_email(self):
344 cleaned_data = self.cleaned_data
345 email = cleaned_data.get("email")
346 if email:
347 try:
595ab4d6 348 Utilisateur.objects.get(courriel=email, actif=True)
0e9597af 349 except:
89da853e 350 raise forms.ValidationError("Cette adresse n'existe pas dans notre base de données.")
e427f068
AJ
351 return email
352
353class NewPasswordForm(forms.Form):
354 password = forms.CharField(widget=forms.PasswordInput(), required=True, label="Mot de passe")
355 password_repeat = forms.CharField(widget=forms.PasswordInput(), required=True, label="Confirmez mot de passe")
92990258 356
e427f068
AJ
357 def clean_password_repeat(self):
358 cleaned_data = self.cleaned_data
359 password = cleaned_data.get("password")
360 password_repeat = cleaned_data.get("password_repeat")
361 if password and password_repeat:
362 if password != password_repeat:
363 raise forms.ValidationError("Les mots de passe ne concordent pas")
364 return password_repeat
0e9597af 365