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