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