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