Ajout de questions à la section "Activités en Francophonie"
[auf_savoirs_en_partage_django.git] / auf_savoirs_en_partage / chercheurs / models.py
1 # -*- encoding: utf-8 -*-
2 import hashlib
3 from authentification import get_django_user_for_email
4 from datamaster_modeles.models import *
5 from django.conf import settings
6 from django.contrib.auth.models import User
7 from django.db import models
8 from django.db.models import Q
9 from django.utils.encoding import smart_str
10 from django.utils.hashcompat import sha_constructor
11 from djangosphinx.models import SphinxSearch
12 from savoirs.models import Discipline, SEPManager, SEPSphinxQuerySet, SEPQuerySet
13
14 GENRE_CHOICES = (('m', 'Homme'), ('f', 'Femme'))
15 class Personne(models.Model):
16 user = models.OneToOneField(User, null=True, editable=False)
17 salutation = models.CharField(max_length=128, null=True, blank=True)
18 nom = models.CharField(max_length=255)
19 prenom = models.CharField(max_length=128, verbose_name='prénom')
20 courriel = models.EmailField(max_length=128, verbose_name="adresse électronique")
21 fonction = models.CharField(max_length=128, null=True, blank=True)
22 date_naissance = models.DateField(null=True, blank=True)
23 sousfonction = models.CharField(max_length=128, null=True, blank=True, verbose_name='sous-fonction')
24 mobile = models.CharField(max_length=32, null=True, blank=True, verbose_name='numéro de téléphone portable')
25 genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
26 commentaire = models.TextField(verbose_name='commentaires', null=True, blank=True)
27 actif = models.BooleanField(editable=False, default=True)
28
29 def __unicode__(self):
30 return u"%s %s, %s" % (self.prenom, self.nom, self.courriel)
31
32 class Meta:
33 ordering = ["nom", "prenom"]
34
35 def save(self):
36 if self.actif:
37 if self.user:
38 self.user.username = self.courriel
39 self.user.email = self.courriel
40 else:
41 self.user = get_django_user_for_email(self.courriel)
42 self.user.last_name = self.nom
43 self.user.first_name = self.prenom
44 else:
45 if self.user:
46 self.user.is_active = False
47 if self.user:
48 self.user.save()
49 super(Personne, self).save()
50
51 class ChercheurQuerySet(SEPQuerySet):
52
53 def filter_groupe(self, groupe):
54 return self.filter(groupes=groupe)
55
56 def filter_pays(self, pays):
57 return self.filter(Q(etablissement__pays=pays) | Q(etablissement_autre_pays=pays))
58
59 def filter_region(self, region):
60 return self.filter(Q(etablissement__pays__region=region) | Q(etablissement_autre_pays__region=region))
61
62 def filter_nord_sud(self, nord_sud):
63 return self.filter(Q(etablissement__pays__nord_sud=nord_sud) | Q(etablissement_autre_pays__nord_sud=nord_sud))
64
65 def filter_statut(self, statut):
66 return self.filter(statut=statut)
67
68 def filter_expert(self):
69 return self.exclude(expertises=None)
70
71 def order_by_nom(self, direction=''):
72 return self.order_by(direction + 'personne__nom', direction + 'personne__prenom', '-date_modification')
73
74 def order_by_etablissement(self, direction=''):
75 return self.extra(select=dict(nom_etablissement='IFNULL(ref_etablissement.nom, chercheurs_chercheur.etablissement_autre_nom)'),
76 order_by=[direction + 'nom_etablissement', '-date_modification'])
77
78 def order_by_pays(self, direction=''):
79 return self.extra(select=dict(
80 pays_etablissement='''(SELECT nom FROM ref_pays
81 WHERE ref_pays.code = IFNULL(ref_etablissement.pays, chercheurs_chercheur.etablissement_autre_pays))'''
82 ), order_by=[direction + 'pays_etablissement', '-date_modification'])
83
84 class ChercheurSphinxQuerySet(SEPSphinxQuerySet):
85
86 def __init__(self, model=None):
87 return SEPSphinxQuerySet.__init__(self, model=model, index='savoirsenpartage_chercheurs',
88 weights=dict(nom=2, prenom=2))
89
90 def filter_region(self, region):
91 return self.filter(region_id=region.id)
92
93 def filter_groupe(self, groupe):
94 return self.filter(groupe_ids=groupe.id)
95
96 def filter_pays(self, pays):
97 return self.filter(pays_id=pays.id)
98
99 NORD_SUD_CODES = {'Nord': 1, 'Sud': 2}
100 def filter_nord_sud(self, nord_sud):
101 return self.filter(nord_sud=self.NORD_SUD_CODES[nord_sud])
102
103 STATUT_CODES = {'enseignant': 1, 'etudiant': 2, 'independant': 3}
104 def filter_statut(self, statut):
105 return self.filter(statut=self.STATUT_CODES[statut])
106
107 def filter_expert(self):
108 return self.filter(expert=True)
109
110 def order_by_nom(self, direction=''):
111 return self.order_by(direction + 'nom_complet', '-date_modification')
112
113 def order_by_etablissement(self, direction=''):
114 return self.order_by(direction + 'etablissement_attr', '-date_modification')
115
116 def order_by_pays(self, direction=''):
117 return self.order_by(direction + 'pays_attr', '-date_modification')
118
119 class ChercheurManager(SEPManager):
120
121 def get_query_set(self):
122 return ChercheurQuerySet(self.model)
123
124 def get_sphinx_query_set(self):
125 return ChercheurSphinxQuerySet(self.model).order_by('-date_modification')
126
127 def filter_region(self, region):
128 """Le filtrage de chercheurs par région n'est pas une recherche texte."""
129 return self.get_query_set().filter_region(region)
130
131 def filter_groupe(self, groupe):
132 return self.get_query_set().filter_groupe(groupe)
133
134 def filter_pays(self, pays):
135 return self.get_query_set().filter_pays(pays)
136
137 def filter_nord_sud(self, nord_sud):
138 return self.get_query_set().filter_nord_sud(nord_sud)
139
140 def filter_statut(self, statut):
141 return self.get_query_set().filter_statut(statut)
142
143 def filter_expert(self):
144 return self.get_query_set().filter_expert()
145
146 def order_by_nom(self, direction=''):
147 return self.get_query_set().order_by_nom(self, direction=direction)
148
149 def order_by_etablissement(self, direction=''):
150 return self.get_query_set().order_by_etablissement(self, direction=direction)
151
152 def order_by_pays(self, direction=''):
153 return self.get_query_set().order_by_pays(self, direction=direction)
154
155 STATUT_CHOICES = (
156 ('enseignant', 'Enseignant-chercheur dans un établissement'),
157 ('etudiant', 'Étudiant-chercheur doctorant'),
158 ('independant', 'Chercheur indépendant docteur')
159 )
160
161 class Chercheur(Personne):
162 RESEAU_INSTITUTIONNEL_CHOICES = (
163 ('AFELSH', 'Association des facultés ou établissements de lettres et sciences humaines des universités d’expression française (AFELSH)'),
164 ('CIDEGEF', 'Conférence internationale des dirigeants des institutions d’enseignement supérieur et de recherche de gestion d’expression française (CIDEGEF)'),
165 ('RIFEFF', 'Réseau international francophone des établissements de formation de formateurs (RIFEFF)'),
166 ('CIDMEF', 'Conférence internationale des doyens des facultés de médecine d’expression française (CIDMEF)'),
167 ('CIDCDF', 'Conférence internationale des doyens des facultés de chirurgie dentaire d’expression totalement ou partiellement française (CIDCDF)'),
168 ('CIFDUF', 'Conférence internationale des facultés de droit ayant en commun l’usage du français (CIFDUF)'),
169 ('CIRUISEF', 'Conférence internationale des responsables des universités et institutions à dominante scientifique et technique d’expression française (CIRUISEF)'),
170 ('Theophraste', 'Réseau Théophraste (Réseau de centres francophones de formation au journalisme)'),
171 ('CIDPHARMEF', 'Conférence internationale des doyens des facultés de pharmacie d’expression française (CIDPHARMEF)'),
172 ('CIDEFA', 'Conférence internationale des directeurs et doyens des établissements supérieurs d’expression française des sciences de l’agriculture et de l’alimentation (CIDEFA)'),
173 ('CITEF', 'Conférence internationale des formations d’ingénieurs et techniciens d’expression française (CITEF)'),
174 ('APERAU', 'Association pour la promotion de l’enseignement et de la recherche en aménagement et urbanisme (APERAU)'),
175 )
176 INSTANCE_AUF_CHOICES = (
177 ('CASSOC', 'Conseil associatif'),
178 ('CA', "Conseil d'administration"),
179 ('CS', 'Conseil scientifique'),
180 ('CRE', "Commission régionale d'experts")
181 )
182
183 nationalite = models.ForeignKey(Pays, null = True, db_column='nationalite', to_field='code',
184 verbose_name = 'nationalité', related_name='nationalite')
185 statut = models.CharField(max_length=36, choices=STATUT_CHOICES)
186 diplome = models.CharField(max_length=255, null=True, verbose_name = 'diplôme le plus élevé')
187 etablissement = models.ForeignKey(Etablissement, db_column='etablissement', null=True, blank=True)
188 etablissement_autre_nom = models.CharField(max_length=255, null=True, blank=True, verbose_name = 'autre établissement')
189 etablissement_autre_pays = models.ForeignKey(Pays, null = True, blank=True, db_column='etablissement_autre_pays',
190 to_field='code', related_name='etablissement_autre_pays',
191 verbose_name = "pays de l'établissement")
192 attestation = models.BooleanField()
193
194 #Domaine
195 thematique = models.ForeignKey(Thematique, db_column='thematique', null=True, verbose_name='thematique')
196 mots_cles = models.CharField(max_length=255, null=True, verbose_name='mots-clés')
197 discipline = models.ForeignKey(Discipline, db_column='discipline', null=True, verbose_name='Discipline')
198 theme_recherche = models.TextField(null=True, blank=True, verbose_name='thèmes de recherche')
199 groupe_recherche = models.CharField(max_length=255, blank=True, verbose_name='groupe de recherche')
200 url_site_web = models.URLField(max_length=255, null=True, blank=True,
201 verbose_name='adresse site Internet', verify_exists=False)
202 url_blog = models.URLField(max_length=255, null=True, blank=True, verbose_name='blog',
203 verify_exists=False)
204 url_reseau_social = models.URLField(
205 max_length=255, null=True, blank=True, verbose_name='Réseau social',
206 verify_exists=False,
207 help_text=u"Vous pouvez indiquer ici l'adresse de votre page personnelle dans votre réseau social préféré (e.g. Facebook, LinkedIn, Twitter, Identica, ...)"
208 )
209
210 groupes = models.ManyToManyField('Groupe', through='ChercheurGroupe', blank=True, verbose_name='Domaines de recherche')
211
212 # Activités en francophonie
213 membre_instance_auf = models.BooleanField(default=False, verbose_name="est ou a déjà été membre d'une instance de l'AUF")
214 membre_instance_auf_nom = models.CharField(max_length=10, blank=True, choices=INSTANCE_AUF_CHOICES, verbose_name="instance")
215 membre_instance_auf_fonction = models.CharField(max_length=255, blank=True, verbose_name="fonction")
216 membre_instance_auf_dates = models.CharField(max_length=255, blank=True, verbose_name="dates")
217 expert_oif = models.BooleanField(default=False, verbose_name="a été sollicité par l'OIF")
218 expert_oif_details = models.CharField(max_length=255, blank=True, verbose_name="détails")
219 expert_oif_dates = models.CharField(max_length=255, blank=True, verbose_name="dates")
220 membre_association_francophone = models.BooleanField(default=False, verbose_name="est membre d'une association francophone")
221 membre_association_francophone_details = models.CharField(max_length=255, blank=True, verbose_name="nom de l'association")
222 membre_reseau_institutionnel = models.BooleanField(
223 default=False, verbose_name="est membre des instances d'un réseau institutionnel de l'AUF"
224 )
225 membre_reseau_institutionnel_nom = models.CharField(
226 max_length=15, choices=RESEAU_INSTITUTIONNEL_CHOICES, blank=True,
227 verbose_name="réseau institutionnel"
228 )
229 membre_reseau_institutionnel_fonction = models.CharField(
230 max_length=255, blank=True, verbose_name="fonction"
231 )
232 membre_reseau_institutionnel_dates = models.CharField(
233 max_length=255, blank=True, verbose_name="dates"
234 )
235
236 # Expertises
237 expertises_auf = models.BooleanField(verbose_name="est disposé à réaliser des expertises pour l'AUF")
238
239 #meta
240 date_creation = models.DateField(auto_now_add=True, db_column='date_creation')
241 date_modification = models.DateField(auto_now=True, db_column='date_modification')
242
243 # Manager
244 objects = ChercheurManager()
245 all_objects = models.Manager()
246
247 def __unicode__(self):
248 return u"%s %s" % (self.nom.upper(), self.prenom.title())
249
250 def statut_display(self):
251 for s in STATUT_CHOICES:
252 if self.statut == s[0]:
253 return s[1]
254 return "-"
255
256 @property
257 def etablissement_display(self):
258 if self.etablissement:
259 return self.etablissement.nom + ', ' + self.etablissement.pays.nom
260 else:
261 return self.etablissement_autre_nom + ', ' + self.etablissement_autre_pays.nom
262
263 @property
264 def pays(self):
265 return self.etablissement.pays if self.etablissement else self.etablissement_autre_pays
266
267 @property
268 def region(self):
269 return self.pays.region
270
271 def save(self):
272 """Si on a donné un établissement membre, on laisse tomber l'autre établissement."""
273 if self.etablissement:
274 self.etablissement_autre_nom = None
275 self.etablissement_autre_pays = None
276 super(Chercheur, self).save()
277
278 def activation_token(self):
279 return sha_constructor(settings.SECRET_KEY + unicode(self.id)).hexdigest()[::2]
280
281 class Publication(models.Model):
282 chercheur = models.ForeignKey(Chercheur, related_name='publications')
283 auteurs = models.CharField(max_length=255, blank=True, verbose_name='auteur(s)')
284 titre = models.CharField(max_length=255, null=True, blank=True, verbose_name='titre')
285 revue = models.CharField(max_length=255, null=True, blank=True, verbose_name='revue')
286 annee = models.IntegerField(null=True, blank=True, verbose_name='année de publication')
287 editeur = models.CharField(max_length=255, null=True, blank=True, verbose_name='éditeur')
288 lieu_edition = models.CharField(max_length=255, null=True, blank=True, verbose_name="lieu d'édition")
289 nb_pages = models.CharField(max_length=255, null=True, blank=True, verbose_name='nombre de pages')
290 url = models.URLField(max_length=255, null=True, blank=True, verbose_name='lien vers la publication', verify_exists=False)
291 #Migration des publications depuis l'ancien repertoire de chercheurs
292 publication_affichage = models.TextField(verbose_name='publication', null=True, blank=True)
293 actif = models.BooleanField(editable=False)
294
295 def __unicode__(self):
296 return self.titre or '(Aucun)'
297
298 def save(self):
299 if self.publication_affichage and (self.auteurs or self.titre or
300 self.revue or self.annee or
301 self.editeur or self.lieu_edition
302 or self.nb_pages or self.url):
303 self.publication_affichage = ''
304 super(Publication, self).save()
305
306 class These(models.Model):
307 chercheur = models.OneToOneField(Chercheur, primary_key=True)
308 titre = models.CharField(max_length=255, verbose_name='Titre')
309 annee = models.IntegerField(verbose_name='Année de soutenance (réalisée ou prévue)')
310 directeur = models.CharField(max_length=255, verbose_name='Directeur')
311 etablissement = models.CharField(max_length=255, verbose_name='Établissement de soutenance')
312 nb_pages = models.IntegerField(verbose_name='Nombre de pages', blank=True, null=True)
313 url = models.URLField(max_length=255, verbose_name='Lien vers la publication', blank=True, verify_exists=False)
314
315 def __unicode__(self):
316 return self.titre
317
318 class Expertise(models.Model):
319 id = models.AutoField(primary_key=True, db_column='id')
320 chercheur = models.ForeignKey(Chercheur, related_name='expertises')
321 nom = models.CharField(max_length=255, null=True, blank=True, verbose_name = "Objet de l'expertise")
322 date = models.CharField(max_length=255, blank=True)
323 lieu = models.CharField(max_length=255, null=True, blank=True, verbose_name = "Lieu de l'expertise")
324 organisme_demandeur = models.CharField(max_length=255, null=True, blank=True, verbose_name = 'Organisme demandeur')
325 organisme_demandeur_visible = models.BooleanField(verbose_name="Afficher l'organisme demandeur")
326 actif = models.BooleanField(editable = False, db_column='actif')
327
328 def __unicode__(self):
329 return u"%s" % (self.nom)
330
331 class Groupe(models.Model):
332 id = models.AutoField(primary_key=True, db_column='id')
333 nom = models.CharField(max_length=255, db_column='nom')
334 url = models.URLField(max_length=255, null=True, blank=True,
335 verbose_name='Site web')
336 liste_diffusion = models.URLField(max_length=255, null=True, blank=True,
337 verbose_name='Liste de diffusion')
338 bulletin = models.URLField(max_length=255, null=True, blank=True,
339 verbose_name='Bulletin')
340 actif = models.BooleanField(editable = False, db_column='actif')
341
342 class Meta:
343 verbose_name = 'domaine de recherche'
344 verbose_name_plural = 'domaines de recherche'
345
346 def __unicode__(self):
347 return u"%s" % (self.nom)
348
349 class ChercheurGroupe(models.Model):
350 id = models.AutoField(primary_key=True, db_column='id')
351 chercheur = models.ForeignKey('Chercheur', db_column='chercheur', editable=False)
352 groupe = models.ForeignKey('Groupe', db_column='groupe')
353 date_inscription = models.DateField(auto_now_add=True)
354 date_modification = models.DateField(auto_now=True)
355 actif = models.BooleanField(editable = False, db_column='actif')
356
357 class Meta:
358 verbose_name = 'adhésion'
359
360 def __unicode__(self):
361 return u"%s - %s" % (self.chercheur, self.groupe)