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