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