Ajout d'un champ "Auteur(s)" aux publications.
[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 ETABLISSEMENT_CHOICES = ((id, nom if len(nom) < 80 else nom[:80] + '...')
51 for id, nom in Etablissement.objects.filter(membre=True).values_list('id', 'nom'))
52
53 membre_instance_auf = forms.ChoiceField(
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",
56 choices=OUI_NON_CHOICES, widget=forms.RadioSelect()
57 )
58 membre_instance_auf_details = forms.CharField(label="Préciser laquelle et votre fonction", required=False)
59 membre_instance_auf_dates = forms.CharField(label="Préciser les dates", required=False)
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)
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
78 etablissement = forms.ChoiceField(label='Etablissement', required=False, choices=chain([('', '---------')], ETABLISSEMENT_CHOICES))
79
80 class Meta:
81 model = Chercheur
82 fields = ('statut', 'diplome', 'etablissement',
83 'etablissement_autre_nom', 'etablissement_autre_pays',
84 'discipline', 'theme_recherche', 'groupe_recherche', 'mots_cles',
85 'url_site_web', 'url_blog', 'url_reseau_social',
86 'membre_instance_auf', 'membre_instance_auf_details', 'membre_instance_auf_dates',
87 'expert_oif', 'expert_oif_details', 'expert_oif_dates',
88 'membre_association_francophone',
89 'membre_association_francophone_details',
90 'membre_reseau_institutionnel', 'membre_reseau_institutionnel_details',
91 'membre_reseau_institutionnel_dates')
92
93 def clean_membre_instance_auf(self):
94 return bool(int(self.cleaned_data['membre_instance_auf']))
95
96 def clean_membre_instance_auf_details(self):
97 membre = self.cleaned_data.get('membre_instance_auf')
98 details = self.cleaned_data.get('membre_instance_auf_details')
99 if membre and not details:
100 raise forms.ValidationError('Veuillez préciser')
101 return details
102
103 def clean_membre_instance_auf_dates(self):
104 membre = self.cleaned_data.get('membre_instance_auf')
105 dates = self.cleaned_data.get('membre_instance_auf_dates')
106 if membre and not dates:
107 raise forms.ValidationError('Veuillez préciser les dates')
108 return dates
109
110 def clean_expert_oif(self):
111 return bool(int(self.cleaned_data['expert_oif']))
112
113 def clean_expert_oif_details(self):
114 expert = self.cleaned_data.get('expert_oif')
115 details = self.cleaned_data.get('expert_oif_details')
116 if expert and not details:
117 raise forms.ValidationError('Veuillez préciser')
118 return details
119
120 def clean_expert_oif_dates(self):
121 expert = self.cleaned_data.get('expert_oif')
122 dates = self.cleaned_data.get('expert_oif_dates')
123 if expert and not dates:
124 raise forms.ValidationError('Veuillez préciser les dates')
125 return dates
126
127 def clean_membre_association_francophone(self):
128 return bool(int(self.cleaned_data['membre_association_francophone']))
129
130 def clean_membre_association_francophone_details(self):
131 membre = self.cleaned_data.get('membre_association_francophone')
132 details = self.cleaned_data.get('membre_association_francophone_details')
133 if membre and not details:
134 raise forms.ValidationError('Veuillez préciser')
135 return details
136
137 def clean_membre_reseau_institutionnel(self):
138 return bool(int(self.cleaned_data['membre_reseau_institutionnel']))
139
140 def clean_membre_reseau_institutionnel_details(self):
141 membre = self.cleaned_data.get('membre_reseau_institutionnel')
142 details = self.cleaned_data.get('membre_reseau_institutionnel_details')
143 if membre and not details:
144 raise forms.ValidationError('Veuillez préciser')
145 return details
146
147 def clean_membre_reseau_institutionnel_dates(self):
148 membre = self.cleaned_data.get('membre_reseau_institutionnel')
149 dates = self.cleaned_data.get('membre_reseau_institutionnel_dates')
150 if membre and not dates:
151 raise forms.ValidationError('Veuillez préciser les dates')
152 return dates
153
154 def clean_etablissement(self):
155 etablissement = self.cleaned_data['etablissement']
156 if etablissement:
157 return Etablissement.objects.get(id=etablissement)
158
159 def clean(self):
160 etablissement = self.cleaned_data['etablissement']
161 etablissement_autre_nom = self.cleaned_data['etablissement_autre_nom']
162 etablissement_autre_pays = self.cleaned_data['etablissement_autre_pays']
163 if not etablissement:
164 if not etablissement_autre_nom:
165 self._errors['etablissement'] = self.error_class([u"Vous devez renseigner l'établissement"])
166 elif not etablissement_autre_pays:
167 self._errors['etablissement_autre_pays'] = self.error_class([u"Vous devez renseigner le pays de l'établissement"])
168 return self.cleaned_data
169
170 class ChercheurInscriptionForm(ChercheurForm):
171 attestation = forms.BooleanField(
172 required=True,
173 label="J'atteste sur l'honneur l'exactitude des renseignements fournis sur le formulaire d'inscription et j'accepte leur publication en ligne."
174 )
175
176 class GroupesForm(forms.Form):
177 """Formulaire qui associe des groupes à un chercheur."""
178 groupes = forms.ModelMultipleChoiceField(
179 queryset=Groupe.objects.all(),
180 label='Domaines de recherche', required=False,
181 help_text="Maintenez appuyé « Ctrl », ou « Commande (touche pomme) » sur un Mac, pour en sélectionner plusieurs."
182 )
183
184 def __init__(self, data=None, prefix=None, chercheur=None):
185 self.chercheur = chercheur
186 initial = {}
187 if chercheur:
188 initial['groupes'] = chercheur.groupes.values_list('id', flat=True)
189 super(GroupesForm, self).__init__(data=data, prefix=prefix, initial=initial)
190
191 def save(self):
192 if self.is_valid():
193 groupes = self.cleaned_data['groupes']
194 ChercheurGroupe.objects.filter(chercheur=self.chercheur).exclude(groupe__in=groupes).delete()
195 for g in groupes:
196 ChercheurGroupe.objects.get_or_create(chercheur=self.chercheur, groupe=g, actif=1)
197
198 class PublicationForm(forms.ModelForm):
199 class Meta:
200 model = Publication
201 fields = ('auteurs', 'titre', 'revue', 'annee', 'editeur', 'lieu_edition', 'nb_pages', 'url', 'publication_affichage')
202
203 PublicationFormSet = inlineformset_factory(Chercheur, Publication, form=PublicationForm, extra=1)
204
205 class TheseForm(forms.ModelForm):
206 class Meta:
207 model = These
208 fields = ('titre', 'annee', 'directeur', 'etablissement', 'nb_pages', 'url')
209
210 class ExpertiseForm(forms.ModelForm):
211 organisme_demandeur_visible = forms.ChoiceField(
212 label="Voulez-vous que l'organisme demandeur soit visible sur votre fiche?",
213 choices=OUI_NON_CHOICES, widget=forms.RadioSelect(), required=False
214 )
215 class Meta:
216 model = Expertise
217 fields = ('nom', 'date', 'organisme_demandeur', 'organisme_demandeur_visible')
218
219 def clean_organisme_demandeur_visible(self):
220 value = self.cleaned_data['organisme_demandeur_visible']
221 return bool(int(value)) if value else False
222
223 ExpertiseFormSet = inlineformset_factory(Chercheur, Expertise, form=ExpertiseForm, extra=1)
224
225 class ChercheurFormGroup(object):
226 """Groupe de formulaires nécessaires pour l'inscription et l'édition
227 d'un chercheur."""
228
229 def __init__(self, data=None, chercheur=None):
230 personne_form_class = PersonneInscriptionForm if chercheur is None else PersonneForm
231 chercheur_form_class = ChercheurInscriptionForm if chercheur is None else ChercheurForm
232 self.chercheur = chercheur_form_class(data=data, prefix='chercheur', instance=chercheur)
233 self.groupes = GroupesForm(data=data, prefix='chercheur', chercheur=chercheur)
234 self.personne = personne_form_class(data=data, prefix='personne', instance=chercheur and chercheur.personne.utilisateur)
235 self.expertises = ExpertiseFormSet(data=data, prefix='expertise', instance=chercheur)
236 self.these = TheseForm(data=data, prefix='these', instance=chercheur and chercheur.these)
237 self.publications = PublicationFormSet(data=data, prefix='publication', instance=chercheur)
238
239 @property
240 def has_errors(self):
241 return bool(self.chercheur.errors or self.personne.errors or self.groupes.errors or
242 self.these.errors or self.publications.errors or self.expertises.errors)
243
244 def is_valid(self):
245 return self.chercheur.is_valid() and self.personne.is_valid() and self.groupes.is_valid() and \
246 self.these.is_valid() and self.publications.is_valid() and self.expertises.is_valid()
247
248 def save(self):
249 if self.is_valid():
250
251 chercheur = self.chercheur.instance
252
253 # Enregistrer d'abord les clés étrangères car on doit les stocker dans
254 # l'objet chercheur.
255 chercheur.personne = self.personne.save()
256
257 # Puis enregistrer le chercheur lui-même.
258 self.chercheur.save()
259
260 # Puis les objets qui ont des clés étrangères vers nous
261 # puisqu'on a besoin d'un id.
262 self.groupes.chercheur = chercheur
263 self.groupes.save()
264 self.these.instance.chercheur = chercheur
265 self.these.save()
266 self.publications.instance = chercheur
267 self.publications.save()
268 self.expertises.instance = chercheur
269 self.expertises.save()
270
271 class RepertoireSearchForm (forms.Form):
272 q = forms.CharField(required=False, label="Rechercher dans tous les champs")
273 nom = forms.CharField(required=False, label="Nom")
274 domaine = forms.ModelChoiceField(queryset=Groupe.objects.all(), required=False, label="Domaine de recherche", empty_label="Tous")
275 groupe_recherche = forms.CharField(required=False, label="Groupe de recherche",
276 help_text="ou Laboratoire, ou Groupement inter-universitaire")
277 statut = forms.ChoiceField(choices=(('','Tous'),)+STATUT_CHOICES+(('expert','Expert'),), required=False, label="Statut")
278 discipline = forms.ModelChoiceField(queryset=Discipline.objects.all(), required=False, label="Discipline", empty_label="Toutes")
279 pays = forms.ModelChoiceField(queryset=Pays.objects.all(), required=False, label="Pays", empty_label="Tous")
280 region = forms.ModelChoiceField(queryset=Region.objects.all(), required=False, label="Région", empty_label="Toutes",
281 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.")
282 nord_sud = forms.ChoiceField(choices=(('', 'Tous'), ('Nord', 'Nord'), ('Sud', 'Sud')), required=False, label="Nord/Sud",
283 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 les plus développés, par Sud les pays en voie de développement.")
284
285 def __init__(self, data=None, region=None):
286 super(RepertoireSearchForm, self).__init__(data)
287 if region:
288 pays = self.fields['pays']
289 pays.queryset = pays.queryset.filter(region=region)
290
291 def get_query_set(self):
292 qs = Chercheur.objects.all()
293 if self.is_valid():
294 nom = self.cleaned_data['nom']
295 if nom:
296 qs = qs.search_nom(nom)
297 domaine = self.cleaned_data["domaine"]
298 if domaine:
299 qs = qs.filter(groupes=domaine)
300 groupe_recherche = self.cleaned_data['groupe_recherche']
301 if groupe_recherche:
302 for word in groupe_recherche.split():
303 qs = qs.filter(groupe_recherche__icontains=word)
304 q = self.cleaned_data["q"]
305 if q:
306 qs = qs.search(q)
307 statut = self.cleaned_data["statut"]
308 if statut:
309 if statut == "expert":
310 qs = qs.exclude(expertises=None)
311 else:
312 qs = qs.filter(statut=statut)
313 discipline = self.cleaned_data['discipline']
314 if discipline:
315 qs = qs.filter_discipline(discipline)
316 region = self.cleaned_data['region']
317 if region:
318 qs = qs.filter_region(region)
319 pays = self.cleaned_data["pays"]
320 if pays:
321 qs = qs.filter(Q(etablissement__pays=pays) | Q(etablissement_autre_pays=pays))
322 nord_sud = self.cleaned_data['nord_sud']
323 if nord_sud:
324 qs = qs.filter(Q(etablissement__pays__nord_sud=nord_sud) | Q(etablissement_autre_pays__nord_sud=nord_sud))
325 return qs
326
327 class SendPasswordForm(forms.Form):
328 email = forms.EmailField(required=True, label="Adresse électronique")
329 def clean_email(self):
330 cleaned_data = self.cleaned_data
331 email = cleaned_data.get("email")
332 if email:
333 try:
334 Utilisateur.objects.get(courriel=email, actif=True)
335 except:
336 raise forms.ValidationError("Cette adresse n'existe pas dans notre base de données.")
337 return email
338
339 class NewPasswordForm(forms.Form):
340 password = forms.CharField(widget=forms.PasswordInput(), required=True, label="Mot de passe")
341 password_repeat = forms.CharField(widget=forms.PasswordInput(), required=True, label="Confirmez mot de passe")
342
343 def clean_password_repeat(self):
344 cleaned_data = self.cleaned_data
345 password = cleaned_data.get("password")
346 password_repeat = cleaned_data.get("password_repeat")
347 if password and password_repeat:
348 if password != password_repeat:
349 raise forms.ValidationError("Les mots de passe ne concordent pas")
350 return password_repeat
351