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