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