Commit | Line | Data |
---|---|---|
932eef9a | 1 | # -*- encoding: utf-8 -*- |
a7b16ec9 | 2 | import hashlib |
932eef9a | 3 | from django import forms |
3efbacbe | 4 | from django.db.models import Q |
ee8b3a49 | 5 | from django.forms.models import inlineformset_factory |
fbcbdde0 | 6 | from itertools import chain |
932eef9a | 7 | from models import * |
13146d99 | 8 | |
c073c94d EMS |
9 | OUI_NON_CHOICES = (('1', 'Oui'), ('0', 'Non')) |
10 | ||
932eef9a | 11 | class PersonneForm(forms.ModelForm): |
c18af6bd | 12 | genre = forms.ChoiceField(widget=forms.RadioSelect(), choices=GENRE_CHOICES) |
e4d01d1d | 13 | |
932eef9a | 14 | class Meta: |
dab519fa | 15 | model = Utilisateur |
e4d01d1d EMS |
16 | fields = ('nom', 'prenom', 'courriel', 'genre') |
17 | ||
595ab4d6 EMS |
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'] | |
27775adc EMS |
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(): | |
595ab4d6 EMS |
26 | raise forms.ValidationError('Il existe déjà une fiche pour cette adresse électronique') |
27 | return courriel | |
28 | ||
e4d01d1d EMS |
29 | class PersonneInscriptionForm(PersonneForm): |
30 | password = forms.CharField(widget=forms.PasswordInput(), label="Mot de passe") | |
ea51135b | 31 | password_confirmation = forms.CharField(widget=forms.PasswordInput(), label="Confirmez votre mot de passe") |
e4d01d1d EMS |
32 | |
33 | class Meta(PersonneForm.Meta): | |
ea51135b | 34 | fields = ('nom', 'prenom', 'courriel', 'password', 'password_confirmation', 'genre') |
5ecd9e43 | 35 | |
ea51135b EMS |
36 | def clean_password_confirmation(self): |
37 | """S'assurer que le mot de passe et la confirmation sont identiques.""" | |
fd559765 | 38 | password = self.cleaned_data.get('password') |
92990258 | 39 | confirmation = self.cleaned_data.get('password_confirmation') |
ea51135b EMS |
40 | if password != confirmation: |
41 | raise forms.ValidationError('Les deux mots de passe ne correspondent pas.') | |
92990258 EMS |
42 | return confirmation |
43 | ||
44 | def save(self): | |
45 | self.instance.set_password(self.cleaned_data['password']) | |
46 | return super(PersonneInscriptionForm, self).save() | |
ea51135b | 47 | |
932eef9a | 48 | class ChercheurForm(forms.ModelForm): |
a7b16ec9 | 49 | """Formulaire d'édition d'un chercheur.""" |
a7b16ec9 | 50 | membre_instance_auf = forms.ChoiceField( |
a7b16ec9 EMS |
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", | |
c073c94d | 53 | choices=OUI_NON_CHOICES, widget=forms.RadioSelect() |
a7b16ec9 | 54 | ) |
614b3269 | 55 | membre_instance_auf_details = forms.CharField(label="Préciser laquelle et votre fonction", required=False) |
a7b16ec9 | 56 | membre_instance_auf_dates = forms.CharField(label="Préciser les dates", required=False) |
614b3269 EMS |
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) | |
c073c94d EMS |
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 | ||
219710da EMS |
75 | etablissement = forms.CharField(label='Etablissement', required=True) |
76 | pays_etablissement = forms.ModelChoiceField(label='Pays', queryset=Pays.objects.all(), required=True) | |
a7b16ec9 | 77 | |
cb591fb3 EMS |
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 | ||
332975c3 EMS |
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 | ||
932eef9a AJ |
89 | class Meta: |
90 | model = Chercheur | |
219710da EMS |
91 | fields = ('statut', 'diplome', 'discipline', 'theme_recherche', |
92 | 'groupe_recherche', 'mots_cles', 'url_site_web', | |
93 | 'url_blog', 'url_reseau_social', 'membre_instance_auf', | |
94 | 'membre_instance_auf_details', | |
95 | 'membre_instance_auf_dates', 'expert_oif', | |
96 | 'expert_oif_details', 'expert_oif_dates', | |
614b3269 EMS |
97 | 'membre_association_francophone', |
98 | 'membre_association_francophone_details', | |
219710da EMS |
99 | 'membre_reseau_institutionnel', |
100 | 'membre_reseau_institutionnel_details', | |
cb591fb3 | 101 | 'membre_reseau_institutionnel_dates', 'expertises_auf') |
7c596de2 | 102 | |
219710da EMS |
103 | def __init__(self, data=None, prefix=None, instance=None): |
104 | if instance is not None: | |
105 | initial = {} | |
106 | if instance.etablissement: | |
107 | initial['etablissement'] = instance.etablissement.nom | |
108 | initial['pays_etablissement'] = instance.etablissement.pays_id | |
109 | else: | |
110 | initial['etablissement'] = instance.etablissement_autre_nom | |
111 | initial['pays_etablissement'] = instance.etablissement_autre_pays_id | |
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'] | |
119 | try: | |
120 | etablissement = Etablissement.objects.get(nom=nom_etablissement, pays=pays_etablissement) | |
121 | self.instance.etablissement = etablissement | |
122 | self.instance.etablissement_autre = '' | |
123 | self.instance.etablissement_autre_pays = None | |
124 | except Etablissement.DoesNotExist: | |
125 | self.instance.etablissement = None | |
126 | self.instance.etablissement_autre_nom = nom_etablissement | |
127 | self.instance.etablissement_autre_pays = pays_etablissement | |
128 | super(ChercheurForm, self).save() | |
129 | ||
c073c94d EMS |
130 | def clean_membre_instance_auf(self): |
131 | return bool(int(self.cleaned_data['membre_instance_auf'])) | |
132 | ||
614b3269 EMS |
133 | def clean_membre_instance_auf_details(self): |
134 | membre = self.cleaned_data.get('membre_instance_auf') | |
135 | details = self.cleaned_data.get('membre_instance_auf_details') | |
136 | if membre and not details: | |
137 | raise forms.ValidationError('Veuillez préciser') | |
138 | return details | |
139 | ||
c073c94d EMS |
140 | def clean_membre_instance_auf_dates(self): |
141 | membre = self.cleaned_data.get('membre_instance_auf') | |
142 | dates = self.cleaned_data.get('membre_instance_auf_dates') | |
143 | if membre and not dates: | |
144 | raise forms.ValidationError('Veuillez préciser les dates') | |
145 | return dates | |
146 | ||
147 | def clean_expert_oif(self): | |
148 | return bool(int(self.cleaned_data['expert_oif'])) | |
149 | ||
614b3269 EMS |
150 | def clean_expert_oif_details(self): |
151 | expert = self.cleaned_data.get('expert_oif') | |
152 | details = self.cleaned_data.get('expert_oif_details') | |
153 | if expert and not details: | |
154 | raise forms.ValidationError('Veuillez préciser') | |
155 | return details | |
156 | ||
157 | def clean_expert_oif_dates(self): | |
158 | expert = self.cleaned_data.get('expert_oif') | |
159 | dates = self.cleaned_data.get('expert_oif_dates') | |
160 | if expert and not dates: | |
161 | raise forms.ValidationError('Veuillez préciser les dates') | |
162 | return dates | |
163 | ||
c073c94d EMS |
164 | def clean_membre_association_francophone(self): |
165 | return bool(int(self.cleaned_data['membre_association_francophone'])) | |
166 | ||
167 | def clean_membre_association_francophone_details(self): | |
168 | membre = self.cleaned_data.get('membre_association_francophone') | |
169 | details = self.cleaned_data.get('membre_association_francophone_details') | |
170 | if membre and not details: | |
171 | raise forms.ValidationError('Veuillez préciser') | |
172 | return details | |
173 | ||
174 | def clean_membre_reseau_institutionnel(self): | |
175 | return bool(int(self.cleaned_data['membre_reseau_institutionnel'])) | |
176 | ||
177 | def clean_membre_reseau_institutionnel_details(self): | |
178 | membre = self.cleaned_data.get('membre_reseau_institutionnel') | |
179 | details = self.cleaned_data.get('membre_reseau_institutionnel_details') | |
180 | if membre and not details: | |
181 | raise forms.ValidationError('Veuillez préciser') | |
182 | return details | |
183 | ||
184 | def clean_membre_reseau_institutionnel_dates(self): | |
185 | membre = self.cleaned_data.get('membre_reseau_institutionnel') | |
186 | dates = self.cleaned_data.get('membre_reseau_institutionnel_dates') | |
187 | if membre and not dates: | |
188 | raise forms.ValidationError('Veuillez préciser les dates') | |
189 | return dates | |
190 | ||
cb591fb3 EMS |
191 | def clean_expertises_auf(self): |
192 | return bool(int(self.cleaned_data['expertises_auf'])) | |
193 | ||
d160d3fa EMS |
194 | class ChercheurInscriptionForm(ChercheurForm): |
195 | attestation = forms.BooleanField( | |
196 | required=True, | |
197 | label="J'atteste sur l'honneur l'exactitude des renseignements fournis sur le formulaire d'inscription et j'accepte leur publication en ligne." | |
198 | ) | |
199 | ||
a7b16ec9 EMS |
200 | class GroupesForm(forms.Form): |
201 | """Formulaire qui associe des groupes à un chercheur.""" | |
e4d01d1d EMS |
202 | groupes = forms.ModelMultipleChoiceField( |
203 | queryset=Groupe.objects.all(), | |
204 | label='Domaines de recherche', required=False, | |
205 | help_text="Maintenez appuyé « Ctrl », ou « Commande (touche pomme) » sur un Mac, pour en sélectionner plusieurs." | |
206 | ) | |
a7b16ec9 EMS |
207 | |
208 | def __init__(self, data=None, prefix=None, chercheur=None): | |
209 | self.chercheur = chercheur | |
210 | initial = {} | |
211 | if chercheur: | |
212 | initial['groupes'] = chercheur.groupes.values_list('id', flat=True) | |
213 | super(GroupesForm, self).__init__(data=data, prefix=prefix, initial=initial) | |
214 | ||
215 | def save(self): | |
216 | if self.is_valid(): | |
217 | groupes = self.cleaned_data['groupes'] | |
218 | ChercheurGroupe.objects.filter(chercheur=self.chercheur).exclude(groupe__in=groupes).delete() | |
219 | for g in groupes: | |
220 | ChercheurGroupe.objects.get_or_create(chercheur=self.chercheur, groupe=g, actif=1) | |
221 | ||
00755d9b AJ |
222 | class PublicationForm(forms.ModelForm): |
223 | class Meta: | |
224 | model = Publication | |
1df3737b | 225 | fields = ('auteurs', 'titre', 'revue', 'annee', 'editeur', 'lieu_edition', 'nb_pages', 'url', 'publication_affichage') |
f810842d | 226 | |
595ab4d6 EMS |
227 | PublicationFormSet = inlineformset_factory(Chercheur, Publication, form=PublicationForm, extra=1) |
228 | ||
229 | class TheseForm(forms.ModelForm): | |
e8e9e4fd | 230 | class Meta: |
595ab4d6 EMS |
231 | model = These |
232 | fields = ('titre', 'annee', 'directeur', 'etablissement', 'nb_pages', 'url') | |
2a36714f | 233 | |
5b9abc81 | 234 | class ExpertiseForm(forms.ModelForm): |
c073c94d EMS |
235 | organisme_demandeur_visible = forms.ChoiceField( |
236 | label="Voulez-vous que l'organisme demandeur soit visible sur votre fiche?", | |
237 | choices=OUI_NON_CHOICES, widget=forms.RadioSelect(), required=False | |
238 | ) | |
5b9abc81 AJ |
239 | class Meta: |
240 | model = Expertise | |
241 | fields = ('nom', 'date', 'organisme_demandeur', 'organisme_demandeur_visible') | |
dab519fa | 242 | |
b16bcbaf | 243 | def clean_organisme_demandeur_visible(self): |
6504a7e5 EMS |
244 | value = self.cleaned_data['organisme_demandeur_visible'] |
245 | return bool(int(value)) if value else False | |
b16bcbaf | 246 | |
ee8b3a49 EMS |
247 | ExpertiseFormSet = inlineformset_factory(Chercheur, Expertise, form=ExpertiseForm, extra=1) |
248 | ||
a7b16ec9 EMS |
249 | class ChercheurFormGroup(object): |
250 | """Groupe de formulaires nécessaires pour l'inscription et l'édition | |
251 | d'un chercheur.""" | |
00755d9b | 252 | |
a7b16ec9 | 253 | def __init__(self, data=None, chercheur=None): |
e4d01d1d | 254 | personne_form_class = PersonneInscriptionForm if chercheur is None else PersonneForm |
d160d3fa EMS |
255 | chercheur_form_class = ChercheurInscriptionForm if chercheur is None else ChercheurForm |
256 | self.chercheur = chercheur_form_class(data=data, prefix='chercheur', instance=chercheur) | |
a7b16ec9 | 257 | self.groupes = GroupesForm(data=data, prefix='chercheur', chercheur=chercheur) |
92990258 | 258 | self.personne = personne_form_class(data=data, prefix='personne', instance=chercheur and chercheur.personne.utilisateur) |
ee8b3a49 | 259 | self.expertises = ExpertiseFormSet(data=data, prefix='expertise', instance=chercheur) |
595ab4d6 EMS |
260 | self.these = TheseForm(data=data, prefix='these', instance=chercheur and chercheur.these) |
261 | self.publications = PublicationFormSet(data=data, prefix='publication', instance=chercheur) | |
a7b16ec9 EMS |
262 | |
263 | @property | |
264 | def has_errors(self): | |
265 | return bool(self.chercheur.errors or self.personne.errors or self.groupes.errors or | |
595ab4d6 | 266 | self.these.errors or self.publications.errors or self.expertises.errors) |
a7b16ec9 EMS |
267 | |
268 | def is_valid(self): | |
269 | return self.chercheur.is_valid() and self.personne.is_valid() and self.groupes.is_valid() and \ | |
595ab4d6 | 270 | self.these.is_valid() and self.publications.is_valid() and self.expertises.is_valid() |
a7b16ec9 EMS |
271 | |
272 | def save(self): | |
273 | if self.is_valid(): | |
274 | ||
275 | chercheur = self.chercheur.instance | |
276 | ||
277 | # Enregistrer d'abord les clés étrangères car on doit les stocker dans | |
278 | # l'objet chercheur. | |
279 | chercheur.personne = self.personne.save() | |
a7b16ec9 EMS |
280 | |
281 | # Puis enregistrer le chercheur lui-même. | |
282 | self.chercheur.save() | |
283 | ||
595ab4d6 EMS |
284 | # Puis les objets qui ont des clés étrangères vers nous |
285 | # puisqu'on a besoin d'un id. | |
a7b16ec9 EMS |
286 | self.groupes.chercheur = chercheur |
287 | self.groupes.save() | |
595ab4d6 EMS |
288 | self.these.instance.chercheur = chercheur |
289 | self.these.save() | |
290 | self.publications.instance = chercheur | |
291 | self.publications.save() | |
3820b46e EMS |
292 | self.expertises.instance = chercheur |
293 | self.expertises.save() | |
5b9abc81 | 294 | |
13146d99 | 295 | class RepertoireSearchForm (forms.Form): |
f0692c02 | 296 | q = forms.CharField(required=False, label="Rechercher dans tous les champs") |
3efbacbe | 297 | nom = forms.CharField(required=False, label="Nom") |
0e9597af | 298 | domaine = forms.ModelChoiceField(queryset=Groupe.objects.all(), required=False, label="Domaine de recherche", empty_label="Tous") |
bc415771 EMS |
299 | groupe_recherche = forms.CharField(required=False, label="Groupe de recherche", |
300 | help_text="ou Laboratoire, ou Groupement inter-universitaire") | |
6bd49ff1 | 301 | statut = forms.ChoiceField(choices=(('','Tous'),)+STATUT_CHOICES+(('expert','Expert'),), required=False, label="Statut") |
f0692c02 | 302 | discipline = forms.ModelChoiceField(queryset=Discipline.objects.all(), required=False, label="Discipline", empty_label="Toutes") |
3eba0476 | 303 | pays = forms.ModelChoiceField(queryset=Pays.objects.all(), required=False, label="Pays", empty_label="Tous") |
bc415771 EMS |
304 | region = forms.ModelChoiceField(queryset=Region.objects.all(), required=False, label="Région", empty_label="Toutes", |
305 | 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.") | |
bc415771 | 306 | nord_sud = forms.ChoiceField(choices=(('', 'Tous'), ('Nord', 'Nord'), ('Sud', 'Sud')), required=False, label="Nord/Sud", |
5eecdf3e | 307 | 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)") |
c548d94a EMS |
308 | |
309 | def __init__(self, data=None, region=None): | |
310 | super(RepertoireSearchForm, self).__init__(data) | |
311 | if region: | |
312 | pays = self.fields['pays'] | |
313 | pays.queryset = pays.queryset.filter(region=region) | |
314 | ||
3efbacbe | 315 | def get_query_set(self): |
116db1fd | 316 | qs = Chercheur.objects.all() |
3efbacbe EMS |
317 | if self.is_valid(): |
318 | nom = self.cleaned_data['nom'] | |
319 | if nom: | |
116db1fd EMS |
320 | qs = qs.search_nom(nom) |
321 | domaine = self.cleaned_data["domaine"] | |
322 | if domaine: | |
323 | qs = qs.filter(groupes=domaine) | |
d087521a EMS |
324 | groupe_recherche = self.cleaned_data['groupe_recherche'] |
325 | if groupe_recherche: | |
116db1fd EMS |
326 | for word in groupe_recherche.split(): |
327 | qs = qs.filter(groupe_recherche__icontains=word) | |
328 | q = self.cleaned_data["q"] | |
329 | if q: | |
330 | qs = qs.search(q) | |
5212238e EMS |
331 | statut = self.cleaned_data["statut"] |
332 | if statut: | |
333 | if statut == "expert": | |
116db1fd | 334 | qs = qs.exclude(expertises=None) |
5212238e | 335 | else: |
116db1fd EMS |
336 | qs = qs.filter(statut=statut) |
337 | discipline = self.cleaned_data['discipline'] | |
338 | if discipline: | |
339 | qs = qs.filter_discipline(discipline) | |
340 | region = self.cleaned_data['region'] | |
341 | if region: | |
342 | qs = qs.filter_region(region) | |
62eece34 EMS |
343 | pays = self.cleaned_data["pays"] |
344 | if pays: | |
116db1fd | 345 | qs = qs.filter(Q(etablissement__pays=pays) | Q(etablissement_autre_pays=pays)) |
62eece34 EMS |
346 | nord_sud = self.cleaned_data['nord_sud'] |
347 | if nord_sud: | |
116db1fd EMS |
348 | qs = qs.filter(Q(etablissement__pays__nord_sud=nord_sud) | Q(etablissement_autre_pays__nord_sud=nord_sud)) |
349 | return qs | |
3efbacbe | 350 | |
0e9597af | 351 | class SendPasswordForm(forms.Form): |
89da853e | 352 | email = forms.EmailField(required=True, label="Adresse électronique") |
0e9597af AJ |
353 | def clean_email(self): |
354 | cleaned_data = self.cleaned_data | |
355 | email = cleaned_data.get("email") | |
356 | if email: | |
357 | try: | |
595ab4d6 | 358 | Utilisateur.objects.get(courriel=email, actif=True) |
0e9597af | 359 | except: |
89da853e | 360 | raise forms.ValidationError("Cette adresse n'existe pas dans notre base de données.") |
e427f068 AJ |
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") | |
92990258 | 366 | |
e427f068 AJ |
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 | |
0e9597af | 375 |