refs #1117 : index genre
[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
c6efde25
OL
96 GENRE_CODES = dict([(k, i+1) for i, (k, v) in enumerate(GENRE_CHOICES)])
97 def filter_genre(self, genre):
98 return self.filter(genre=self.GENRE_CODES[genre])
99
5212238e
EMS
100 STATUT_CODES = {'enseignant': 1, 'etudiant': 2, 'independant': 3}
101 def filter_statut(self, statut):
102 return self.filter(statut=self.STATUT_CODES[statut])
103
d9da735f
EMS
104 def filter_expert(self):
105 return self.filter(expert=True)
106
acd5cd8f
EMS
107 def order_by_nom(self, direction=''):
108 return self.order_by(direction + 'nom_complet', '-date_modification')
109
110 def order_by_etablissement(self, direction=''):
111 return self.order_by(direction + 'etablissement_attr', '-date_modification')
112
113 def order_by_pays(self, direction=''):
114 return self.order_by(direction + 'pays_attr', '-date_modification')
115
5212238e 116class ChercheurManager(SEPManager):
a2c6bb72
EMS
117
118 def get_query_set(self):
695930dd 119 return ChercheurQuerySet(self.model).filter(actif=True)
a2c6bb72 120
5212238e
EMS
121 def get_sphinx_query_set(self):
122 return ChercheurSphinxQuerySet(self.model).order_by('-date_modification')
5212238e 123
116db1fd 124 def filter_region(self, region):
5212238e 125 """Le filtrage de chercheurs par région n'est pas une recherche texte."""
c1b134f8
EMS
126 return self.get_query_set().filter_region(region)
127
5212238e
EMS
128 def filter_groupe(self, groupe):
129 return self.get_query_set().filter_groupe(groupe)
bae03b7b 130
5212238e
EMS
131 def filter_pays(self, pays):
132 return self.get_query_set().filter_pays(pays)
133
134 def filter_nord_sud(self, nord_sud):
135 return self.get_query_set().filter_nord_sud(nord_sud)
136
a7bf6c5a 137 def filter_genre(self, genre):
634d46da 138 return self.get_query_set().filter_genre(genre=genre)
a7bf6c5a 139
5212238e
EMS
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()
3efbacbe 145
acd5cd8f
EMS
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
bc15119b
EMS
155STATUT_CHOICES = (
156 ('enseignant', 'Enseignant-chercheur dans un établissement'),
157 ('etudiant', 'Étudiant-chercheur doctorant'),
158 ('independant', 'Chercheur indépendant docteur')
159)
160
13ec4813 161class Chercheur(Personne):
bc15119b
EMS
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
00755d9b 183 nationalite = models.ForeignKey(Pays, null = True, db_column='nationalite', to_field='code',
0b0fbbd7 184 verbose_name = 'nationalité', related_name='nationalite')
a4e383ac 185 statut = models.CharField(max_length=36, choices=STATUT_CHOICES)
0b0fbbd7 186 diplome = models.CharField(max_length=255, null=True, verbose_name = 'diplôme le plus élevé')
73cabd75 187 etablissement = models.ForeignKey(Etablissement, db_column='etablissement', null=True, blank=True)
0b0fbbd7 188 etablissement_autre_nom = models.CharField(max_length=255, null=True, blank=True, verbose_name = 'autre établissement')
00755d9b 189 etablissement_autre_pays = models.ForeignKey(Pays, null = True, blank=True, db_column='etablissement_autre_pays',
0b0fbbd7
EMS
190 to_field='code', related_name='etablissement_autre_pays',
191 verbose_name = "pays de l'établissement")
0874e7d1 192 attestation = models.BooleanField()
73cabd75 193
0b0fbbd7
EMS
194 #Domaine
195 thematique = models.ForeignKey(Thematique, db_column='thematique', null=True, verbose_name='thematique')
518d0b44 196 mots_cles = models.CharField(max_length=255, null=True, verbose_name='mots-clés')
0b0fbbd7 197 discipline = models.ForeignKey(Discipline, db_column='discipline', null=True, verbose_name='Discipline')
518d0b44 198 theme_recherche = models.TextField(null=True, blank=True, verbose_name='thèmes de recherche')
0b0fbbd7 199 groupe_recherche = models.CharField(max_length=255, blank=True, verbose_name='groupe de recherche')
219710da
EMS
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)
3c576696
EMS
204 url_reseau_social = models.URLField(
205 max_length=255, null=True, blank=True, verbose_name='Réseau social',
219710da 206 verify_exists=False,
3c576696
EMS
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 )
00755d9b 209
e4d01d1d 210 groupes = models.ManyToManyField('Groupe', through='ChercheurGroupe', blank=True, verbose_name='Domaines de recherche')
932eef9a 211
a7b16ec9 212 # Activités en francophonie
121d9439 213 membre_instance_auf = models.NullBooleanField(verbose_name="est ou a déjà été membre d'une instance de l'AUF")
bc15119b
EMS
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")
a7b16ec9 216 membre_instance_auf_dates = models.CharField(max_length=255, blank=True, verbose_name="dates")
121d9439 217 expert_oif = models.NullBooleanField(verbose_name="a été sollicité par l'OIF")
614b3269
EMS
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")
121d9439 220 membre_association_francophone = models.NullBooleanField(verbose_name="est membre d'une association francophone")
c073c94d 221 membre_association_francophone_details = models.CharField(max_length=255, blank=True, verbose_name="nom de l'association")
121d9439
EMS
222 membre_reseau_institutionnel = models.NullBooleanField(
223 verbose_name="est membre des instances d'un réseau institutionnel de l'AUF"
bc15119b
EMS
224 )
225 membre_reseau_institutionnel_nom = models.CharField(
226 max_length=15, choices=RESEAU_INSTITUTIONNEL_CHOICES, blank=True,
227 verbose_name="réseau institutionnel"
c073c94d 228 )
bc15119b
EMS
229 membre_reseau_institutionnel_fonction = models.CharField(
230 max_length=255, blank=True, verbose_name="fonction"
c073c94d
EMS
231 )
232 membre_reseau_institutionnel_dates = models.CharField(
233 max_length=255, blank=True, verbose_name="dates"
234 )
a7b16ec9 235
cb591fb3 236 # Expertises
121d9439 237 expertises_auf = models.NullBooleanField(verbose_name="est disposé à réaliser des expertises pour l'AUF")
cb591fb3 238
00755d9b
AJ
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
a2c6bb72
EMS
243 # Manager
244 objects = ChercheurManager()
c59dba82 245 all_objects = models.Manager()
a2c6bb72 246
588d6b93 247 def __unicode__(self):
13ec4813 248 return u"%s %s" % (self.nom.upper(), self.prenom.title())
e427f068 249
b57a7362
AJ
250 def statut_display(self):
251 for s in STATUT_CHOICES:
252 if self.statut == s[0]:
253 return s[1]
e427f068 254 return "-"
588d6b93 255
e4d01d1d
EMS
256 @property
257 def etablissement_display(self):
258 if self.etablissement:
259 return self.etablissement.nom + ', ' + self.etablissement.pays.nom
260 else:
67c99fde 261 return self.etablissement_autre_nom + ', ' + self.etablissement_autre_pays.nom
e4d01d1d 262
d32102bd
EMS
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
71e3d741
EMS
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
43ed73e7
EMS
278 def activation_token(self):
279 return sha_constructor(settings.SECRET_KEY + unicode(self.id)).hexdigest()[::2]
280
00755d9b 281class Publication(models.Model):
595ab4d6 282 chercheur = models.ForeignKey(Chercheur, related_name='publications')
1df3737b 283 auteurs = models.CharField(max_length=255, blank=True, verbose_name='auteur(s)')
595ab4d6 284 titre = models.CharField(max_length=255, null=True, blank=True, verbose_name='titre')
1df3737b
EMS
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')
912e3c6c 290 url = models.URLField(max_length=255, null=True, blank=True, verbose_name='lien vers la publication', verify_exists=False)
6befc7c9 291 #Migration des publications depuis l'ancien repertoire de chercheurs
1df3737b 292 publication_affichage = models.TextField(verbose_name='publication', null=True, blank=True)
595ab4d6 293 actif = models.BooleanField(editable=False)
2a36714f
AJ
294
295 def __unicode__(self):
c59dba82 296 return self.titre or '(Aucun)'
2a36714f 297
3eb41a6d
EMS
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
595ab4d6
EMS
306class These(models.Model):
307 chercheur = models.OneToOneField(Chercheur, primary_key=True)
14fd1c3f 308 titre = models.CharField(max_length=255, verbose_name='Titre')
595ab4d6 309 annee = models.IntegerField(verbose_name='Année de soutenance (réalisée ou prévue)')
14fd1c3f 310 directeur = models.CharField(max_length=255, verbose_name='Directeur')
595ab4d6
EMS
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)
912e3c6c 313 url = models.URLField(max_length=255, verbose_name='Lien vers la publication', blank=True, verify_exists=False)
595ab4d6
EMS
314
315 def __unicode__(self):
316 return self.titre
317
2a36714f
AJ
318class Expertise(models.Model):
319 id = models.AutoField(primary_key=True, db_column='id')
ee8b3a49 320 chercheur = models.ForeignKey(Chercheur, related_name='expertises')
356cc691 321 nom = models.CharField(max_length=255, verbose_name = "Objet de l'expertise")
c1234eb8 322 date = models.CharField(max_length=255, blank=True)
ee8b3a49 323 lieu = models.CharField(max_length=255, null=True, blank=True, verbose_name = "Lieu de l'expertise")
b16bcbaf
EMS
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")
2a36714f 326 actif = models.BooleanField(editable = False, db_column='actif')
5b9abc81
AJ
327
328 def __unicode__(self):
329 return u"%s" % (self.nom)
2a36714f 330
932eef9a
AJ
331class Groupe(models.Model):
332 id = models.AutoField(primary_key=True, db_column='id')
333 nom = models.CharField(max_length=255, db_column='nom')
00755d9b
AJ
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')
932eef9a
AJ
340 actif = models.BooleanField(editable = False, db_column='actif')
341
55ef8558
EMS
342 class Meta:
343 verbose_name = 'domaine de recherche'
344 verbose_name_plural = 'domaines de recherche'
345
932eef9a
AJ
346 def __unicode__(self):
347 return u"%s" % (self.nom)
348
349class ChercheurGroupe(models.Model):
73cabd75 350 id = models.AutoField(primary_key=True, db_column='id')
55ef8558 351 chercheur = models.ForeignKey('Chercheur', db_column='chercheur', editable=False)
73cabd75 352 groupe = models.ForeignKey('Groupe', db_column='groupe')
00755d9b
AJ
353 date_inscription = models.DateField(auto_now_add=True)
354 date_modification = models.DateField(auto_now=True)
5ecd9e43 355 actif = models.BooleanField(editable = False, db_column='actif')
2a36714f 356
55ef8558
EMS
357 class Meta:
358 verbose_name = 'adhésion'
359
2a36714f
AJ
360 def __unicode__(self):
361 return u"%s - %s" % (self.chercheur, self.groupe)