Groupe prit en charge dans la recherche globale
[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
fa6a2a07 4from django.contrib.auth.forms import AuthenticationForm as DjangoAuthenticationForm
3efbacbe 5from django.db.models import Q
ee8b3a49 6from django.forms.models import inlineformset_factory
fbcbdde0 7from itertools import chain
932eef9a 8from models import *
13146d99 9
e229761e 10OUI_NON_CHOICES = ((True, 'Oui'), (False, 'Non'))
c073c94d 11
932eef9a 12class ChercheurForm(forms.ModelForm):
a7b16ec9 13 """Formulaire d'édition d'un chercheur."""
13ec4813 14 genre = forms.ChoiceField(widget=forms.RadioSelect(), choices=GENRE_CHOICES)
165da916
EMS
15 afficher_courriel = forms.ChoiceField(
16 label="Afficher mon courriel publiquement sur ce site",
17 choices=OUI_NON_CHOICES, widget=forms.RadioSelect()
18 )
a7b16ec9 19 membre_instance_auf = forms.ChoiceField(
a7b16ec9 20 label="Êtes-vous (ou avez-vous déjà été) membre d'une instance de l'AUF?",
c073c94d 21 choices=OUI_NON_CHOICES, widget=forms.RadioSelect()
a7b16ec9 22 )
bc15119b
EMS
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)
a7b16ec9 28 membre_instance_auf_dates = forms.CharField(label="Préciser les dates", required=False)
614b3269
EMS
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)
c073c94d
EMS
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(
9553bf2d 40 label="Êtes-vous (ou avez-vous déjà été) membre des instances d'un réseau institutionnel de l'AUF?",
c073c94d
EMS
41 choices=OUI_NON_CHOICES, widget=forms.RadioSelect()
42 )
bc15119b
EMS
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")
c073c94d
EMS
49 membre_reseau_institutionnel_dates = forms.CharField(required=False, label="Préciser les dates")
50
e836f6f7 51 pays_etablissement = forms.ModelChoiceField(label="Pays de l'établissement", queryset=Pays.objects.all(), required=True)
593412b3
EMS
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 )
a7b16ec9 56
1d6fac5f
EMS
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"
cb591fb3
EMS
60 )
61
332975c3
EMS
62 theme_recherche = forms.CharField(
63 max_length=1000, label='Thèmes de recherche', help_text='1000 signes maximum',
70534a94 64 error_messages=dict(max_length="Veuillez entrer au maximum %(limit_value)d signes (vous en avez entré %(show_value)d)."),
332975c3
EMS
65 widget=forms.Textarea()
66 )
0874e7d1
EMS
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 )
593412b3
EMS
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 )
332975c3 84
932eef9a
AJ
85 class Meta:
86 model = Chercheur
165da916 87 fields = ('nom', 'prenom', 'genre', 'afficher_courriel', 'adresse_postale', 'telephone',
0a379c77 88 'statut', 'diplome',
bc15119b
EMS
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',
219710da
EMS
93 'membre_instance_auf_dates', 'expert_oif',
94 'expert_oif_details', 'expert_oif_dates',
614b3269
EMS
95 'membre_association_francophone',
96 'membre_association_francophone_details',
219710da 97 'membre_reseau_institutionnel',
bc15119b
EMS
98 'membre_reseau_institutionnel_nom',
99 'membre_reseau_institutionnel_fonction',
1d6fac5f 100 'membre_reseau_institutionnel_dates')
7c596de2 101
219710da
EMS
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
1d6fac5f 111 initial['pas_de_sollicitation_expertises'] = not instance.expertises_auf
219710da
EMS
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']
e76f8899
EMS
119 etablissements = Etablissement.objects.filter(nom=nom_etablissement, pays=pays_etablissement, actif=True)
120 if etablissements.count() > 0:
121 self.instance.etablissement = etablissements[0]
219710da
EMS
122 self.instance.etablissement_autre = ''
123 self.instance.etablissement_autre_pays = None
e76f8899 124 else:
219710da
EMS
125 self.instance.etablissement = None
126 self.instance.etablissement_autre_nom = nom_etablissement
127 self.instance.etablissement_autre_pays = pays_etablissement
1d6fac5f 128 self.instance.expertises_auf = not self.cleaned_data['pas_de_sollicitation_expertises']
219710da
EMS
129 super(ChercheurForm, self).save()
130
13ec4813
EMS
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
165da916
EMS
141
142 def clean_afficher_courriel(self):
e229761e 143 return self.cleaned_data['afficher_courriel'] == 'True'
165da916 144
c073c94d 145 def clean_membre_instance_auf(self):
e229761e 146 return self.cleaned_data['membre_instance_auf'] == 'True'
c073c94d 147
bc15119b 148 def clean_membre_instance_auf_nom(self):
614b3269 149 membre = self.cleaned_data.get('membre_instance_auf')
bc15119b
EMS
150 nom = self.cleaned_data.get('membre_instance_auf_nom')
151 if membre and not nom:
614b3269 152 raise forms.ValidationError('Veuillez préciser')
bc15119b
EMS
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
614b3269 161
c073c94d
EMS
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):
e229761e 170 return self.cleaned_data['expert_oif'] == 'True'
c073c94d 171
614b3269
EMS
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
c073c94d 186 def clean_membre_association_francophone(self):
e229761e 187 return self.cleaned_data['membre_association_francophone'] == 'True'
c073c94d
EMS
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):
e229761e 197 return self.cleaned_data['membre_reseau_institutionnel'] == 'True'
c073c94d 198
bc15119b 199 def clean_membre_reseau_institutionnel_nom(self):
c073c94d 200 membre = self.cleaned_data.get('membre_reseau_institutionnel')
bc15119b
EMS
201 nom = self.cleaned_data.get('membre_reseau_institutionnel_nom')
202 if membre and not nom:
c073c94d 203 raise forms.ValidationError('Veuillez préciser')
bc15119b
EMS
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
c073c94d
EMS
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
13ec4813 220class ChercheurInscriptionForm(ChercheurForm):
13ec4813
EMS
221
222 class Meta(ChercheurForm.Meta):
43ed73e7 223 fields = ChercheurForm.Meta.fields + ('courriel',)
13ec4813 224
a7b16ec9 225class GroupesForm(forms.Form):
5b55252d
PP
226 """Formulaire qui associe des domaines de recherche et groupe de chercheur à un chercheur."""
227 domaines_recherche = forms.ModelMultipleChoiceField(
228 queryset=Groupe.domaine_recherche_objects.all(),
e4d01d1d 229 label='Domaines de recherche', required=False,
593412b3 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."
e4d01d1d 231 )
a7b16ec9 232
5b55252d
PP
233 groupes_chercheur = forms.ModelMultipleChoiceField(
234 queryset=Groupe.groupe_chercheur_objects.all(),
235 label='Groupes de chercheur', required=False,
236 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."
237 )
238
a7b16ec9
EMS
239 def __init__(self, data=None, prefix=None, chercheur=None):
240 self.chercheur = chercheur
241 initial = {}
242 if chercheur:
5b55252d
PP
243 initial['domaines_recherche'] = chercheur.domaines_recherche.values_list('id', flat=True)
244 initial['groupes_chercheur'] = chercheur.groupes_chercheur.values_list('id', flat=True)
a7b16ec9
EMS
245 super(GroupesForm, self).__init__(data=data, prefix=prefix, initial=initial)
246
247 def save(self):
248 if self.is_valid():
5b55252d
PP
249 domaines_recherche = self.cleaned_data['domaines_recherche']
250 ChercheurGroupe.objects.filter(chercheur=self.chercheur).exclude(groupe__groupe_chercheur=True).exclude(groupe__in=domaines_recherche).delete()
251 for dr in domaines_recherche:
d1fec99c
PP
252 cg, created = ChercheurGroupe.objects.get_or_create(chercheur=self.chercheur, groupe=dr)
253 if created:
254 cg.actif = 1
255 cg.save()
5b55252d
PP
256
257 groupes_chercheur = self.cleaned_data['groupes_chercheur']
258 ChercheurGroupe.objects.filter(chercheur=self.chercheur).exclude(groupe__groupe_chercheur=False).exclude(groupe__in=groupes_chercheur).delete()
259 for gc in groupes_chercheur:
d1fec99c
PP
260 cg, created = ChercheurGroupe.objects.get_or_create(chercheur=self.chercheur, groupe=gc)
261 if created:
262 cg.actif = 0
263 cg.save()
a7b16ec9 264
00755d9b
AJ
265class PublicationForm(forms.ModelForm):
266 class Meta:
267 model = Publication
3eb41a6d 268 fields = ('auteurs', 'titre', 'revue', 'annee', 'editeur', 'lieu_edition', 'nb_pages', 'url')
f810842d 269
595ab4d6
EMS
270PublicationFormSet = inlineformset_factory(Chercheur, Publication, form=PublicationForm, extra=1)
271
272class TheseForm(forms.ModelForm):
e8e9e4fd 273 class Meta:
595ab4d6
EMS
274 model = These
275 fields = ('titre', 'annee', 'directeur', 'etablissement', 'nb_pages', 'url')
2a36714f 276
5b9abc81 277class ExpertiseForm(forms.ModelForm):
c073c94d
EMS
278 organisme_demandeur_visible = forms.ChoiceField(
279 label="Voulez-vous que l'organisme demandeur soit visible sur votre fiche?",
280 choices=OUI_NON_CHOICES, widget=forms.RadioSelect(), required=False
281 )
e229761e
EMS
282 nom = forms.CharField(label="Objet de l'expertise", required=False)
283
5b9abc81
AJ
284 class Meta:
285 model = Expertise
286 fields = ('nom', 'date', 'organisme_demandeur', 'organisme_demandeur_visible')
dab519fa 287
b16bcbaf 288 def clean_organisme_demandeur_visible(self):
6504a7e5 289 value = self.cleaned_data['organisme_demandeur_visible']
e229761e 290 return value == 'True'
b16bcbaf 291
ee8b3a49
EMS
292ExpertiseFormSet = inlineformset_factory(Chercheur, Expertise, form=ExpertiseForm, extra=1)
293
a7b16ec9
EMS
294class ChercheurFormGroup(object):
295 """Groupe de formulaires nécessaires pour l'inscription et l'édition
296 d'un chercheur."""
00755d9b 297
a7b16ec9 298 def __init__(self, data=None, chercheur=None):
e61eecfe
EMS
299 try:
300 these = chercheur and chercheur.these
301 except These.DoesNotExist:
302 these = These()
43ed73e7 303 chercheur_form_class = ChercheurInscriptionForm if chercheur is None else ChercheurForm
13ec4813 304 self.chercheur = chercheur_form_class(data=data, prefix='chercheur', instance=chercheur)
a7b16ec9 305 self.groupes = GroupesForm(data=data, prefix='chercheur', chercheur=chercheur)
ee8b3a49 306 self.expertises = ExpertiseFormSet(data=data, prefix='expertise', instance=chercheur)
e61eecfe 307 self.these = TheseForm(data=data, prefix='these', instance=these)
595ab4d6 308 self.publications = PublicationFormSet(data=data, prefix='publication', instance=chercheur)
a7b16ec9
EMS
309
310 @property
311 def has_errors(self):
13ec4813
EMS
312 return bool(self.chercheur.errors or self.groupes.errors or
313 self.these.errors or self.publications.errors or
314 self.expertises.errors)
a7b16ec9
EMS
315
316 def is_valid(self):
13ec4813
EMS
317 return self.chercheur.is_valid() and self.groupes.is_valid() and \
318 self.these.is_valid() and self.publications.is_valid() and \
319 self.expertises.is_valid()
a7b16ec9
EMS
320
321 def save(self):
322 if self.is_valid():
323
13ec4813 324 # Enregistrer d'abord le chercheur lui-même.
a7b16ec9
EMS
325 self.chercheur.save()
326
595ab4d6
EMS
327 # Puis les objets qui ont des clés étrangères vers nous
328 # puisqu'on a besoin d'un id.
13ec4813 329 chercheur = self.chercheur.instance
a7b16ec9
EMS
330 self.groupes.chercheur = chercheur
331 self.groupes.save()
595ab4d6
EMS
332 self.these.instance.chercheur = chercheur
333 self.these.save()
334 self.publications.instance = chercheur
335 self.publications.save()
3820b46e 336 self.expertises.instance = chercheur
e229761e
EMS
337 for expertise in self.expertises.save(commit=False):
338 if expertise.nom:
339 expertise.save()
340 elif expertise.id:
341 expertise.delete()
43ed73e7 342 return self.chercheur.instance
5b9abc81 343
fdcf5874
EMS
344class ChercheurSearchForm(forms.ModelForm):
345 """Formulaire de recherche pour les chercheurs."""
346
347 class Meta:
348 model = ChercheurSearch
5b55252d
PP
349 fields = ['q', 'nom_chercheur', 'domaine', 'groupe_chercheur',
350 'groupe_recherche', 'statut', 'discipline', 'pays',
351 'region', 'nord_sud', 'activites_francophonie', 'genre']
fdcf5874
EMS
352
353class ChercheurSearchEditForm(ChercheurSearchForm):
354 """Formulaire d'édition d'une recherche sauvegardée."""
355
356 class Meta(ChercheurSearchForm.Meta):
4b89a7df 357 fields = ['nom', 'alerte_courriel'] + ChercheurSearchForm.Meta.fields
fdcf5874 358
0e9597af 359class SendPasswordForm(forms.Form):
89da853e 360 email = forms.EmailField(required=True, label="Adresse électronique")
0e9597af
AJ
361 def clean_email(self):
362 cleaned_data = self.cleaned_data
363 email = cleaned_data.get("email")
364 if email:
365 try:
13ec4813 366 Personne.objects.get(courriel=email)
0e9597af 367 except:
89da853e 368 raise forms.ValidationError("Cette adresse n'existe pas dans notre base de données.")
e427f068
AJ
369 return email
370
43ed73e7 371class SetPasswordForm(forms.Form):
e427f068 372 password = forms.CharField(widget=forms.PasswordInput(), required=True, label="Mot de passe")
43ed73e7 373 password_repeat = forms.CharField(widget=forms.PasswordInput(), required=True, label="Confirmez votre mot de passe")
92990258 374
e427f068
AJ
375 def clean_password_repeat(self):
376 cleaned_data = self.cleaned_data
377 password = cleaned_data.get("password")
378 password_repeat = cleaned_data.get("password_repeat")
379 if password and password_repeat:
380 if password != password_repeat:
381 raise forms.ValidationError("Les mots de passe ne concordent pas")
382 return password_repeat
fa6a2a07
EMS
383
384class AuthenticationForm(DjangoAuthenticationForm):
385 username = forms.CharField(label='Courriel')
fdcf5874 386