Ajouté des choix aux instances de l'AUF
[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 mobile = models.CharField(max_length=32, null=True, blank=True, verbose_name='numéro de téléphone portable')
22 genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
23 commentaire = models.TextField(verbose_name='commentaires', null=True, blank=True)
24 actif = models.BooleanField(editable=False, default=True)
25
26 def __unicode__(self):
27 return u"%s %s, %s" % (self.prenom, self.nom, self.courriel)
28
29 class Meta:
30 ordering = ["nom", "prenom"]
31
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
41 class ChercheurQuerySet(SEPQuerySet):
42
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))
51
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))
54
55 def filter_genre(self, genre):
56 return self.filter(genre=genre)
57
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
64 def order_by_nom(self, direction=''):
65 return self.order_by(direction + 'nom', direction + 'prenom', '-date_modification')
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
77 class ChercheurSphinxQuerySet(SEPSphinxQuerySet):
78
79 def __init__(self, model=None):
80 return SEPSphinxQuerySet.__init__(self, model=model, index='savoirsenpartage_chercheurs',
81 weights=dict(nom=2, prenom=2))
82
83 def filter_region(self, region):
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)
91
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 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
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
104 def filter_expert(self):
105 return self.filter(expert=True)
106
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
116 class ChercheurManager(SEPManager):
117
118 def get_query_set(self):
119 return ChercheurQuerySet(self.model).filter(actif=True)
120
121 def get_sphinx_query_set(self):
122 return ChercheurSphinxQuerySet(self.model).order_by('-date_modification')
123
124 def filter_region(self, region):
125 """Le filtrage de chercheurs par région n'est pas une recherche texte."""
126 return self.get_query_set().filter_region(region)
127
128 def filter_groupe(self, groupe):
129 return self.get_query_set().filter_groupe(groupe)
130
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
137 def filter_genre(self, genre):
138 return self.get_query_set().filter_genre(genre=genre)
139
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()
145
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
155 STATUT_CHOICES = (
156 ('enseignant', 'Enseignant-chercheur dans un établissement'),
157 ('etudiant', 'Étudiant-chercheur doctorant'),
158 ('independant', 'Chercheur indépendant docteur')
159 )
160
161 class Chercheur(Personne):
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 ('CR', 'Conférence des recteurs'),
182 ('CNO', "Conseil national d'orientation")
183 )
184
185 nationalite = models.ForeignKey(Pays, null = True, db_column='nationalite', to_field='code',
186 verbose_name = 'nationalité', related_name='nationalite')
187 statut = models.CharField(max_length=36, choices=STATUT_CHOICES)
188 diplome = models.CharField(max_length=255, null=True, verbose_name = 'diplôme le plus élevé')
189 etablissement = models.ForeignKey(Etablissement, db_column='etablissement', null=True, blank=True)
190 etablissement_autre_nom = models.CharField(max_length=255, null=True, blank=True, verbose_name = 'autre établissement')
191 etablissement_autre_pays = models.ForeignKey(Pays, null = True, blank=True, db_column='etablissement_autre_pays',
192 to_field='code', related_name='etablissement_autre_pays',
193 verbose_name = "pays de l'établissement")
194 attestation = models.BooleanField()
195
196 #Domaine
197 thematique = models.ForeignKey(Thematique, db_column='thematique', null=True, verbose_name='thematique')
198 mots_cles = models.CharField(max_length=255, null=True, verbose_name='mots-clés')
199 discipline = models.ForeignKey(Discipline, db_column='discipline', null=True, verbose_name='Discipline')
200 theme_recherche = models.TextField(null=True, blank=True, verbose_name='thèmes de recherche')
201 groupe_recherche = models.CharField(max_length=255, blank=True, verbose_name='groupe de recherche')
202 url_site_web = models.URLField(max_length=255, null=True, blank=True,
203 verbose_name='adresse site Internet', verify_exists=False)
204 url_blog = models.URLField(max_length=255, null=True, blank=True, verbose_name='blog',
205 verify_exists=False)
206 url_reseau_social = models.URLField(
207 max_length=255, null=True, blank=True, verbose_name='Réseau social',
208 verify_exists=False,
209 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, ...)"
210 )
211
212 groupes = models.ManyToManyField('Groupe', through='ChercheurGroupe', blank=True, verbose_name='Domaines de recherche')
213
214 # Activités en francophonie
215 membre_instance_auf = models.NullBooleanField(verbose_name="est ou a déjà été membre d'une instance de l'AUF")
216 membre_instance_auf_nom = models.CharField(max_length=10, blank=True, choices=INSTANCE_AUF_CHOICES, verbose_name="instance")
217 membre_instance_auf_fonction = models.CharField(max_length=255, blank=True, verbose_name="fonction")
218 membre_instance_auf_dates = models.CharField(max_length=255, blank=True, verbose_name="dates")
219 expert_oif = models.NullBooleanField(verbose_name="a été sollicité par l'OIF")
220 expert_oif_details = models.CharField(max_length=255, blank=True, verbose_name="détails")
221 expert_oif_dates = models.CharField(max_length=255, blank=True, verbose_name="dates")
222 membre_association_francophone = models.NullBooleanField(verbose_name="est membre d'une association francophone")
223 membre_association_francophone_details = models.CharField(max_length=255, blank=True, verbose_name="nom de l'association")
224 membre_reseau_institutionnel = models.NullBooleanField(
225 verbose_name="est membre des instances d'un réseau institutionnel de l'AUF"
226 )
227 membre_reseau_institutionnel_nom = models.CharField(
228 max_length=15, choices=RESEAU_INSTITUTIONNEL_CHOICES, blank=True,
229 verbose_name="réseau institutionnel"
230 )
231 membre_reseau_institutionnel_fonction = models.CharField(
232 max_length=255, blank=True, verbose_name="fonction"
233 )
234 membre_reseau_institutionnel_dates = models.CharField(
235 max_length=255, blank=True, verbose_name="dates"
236 )
237
238 # Expertises
239 expertises_auf = models.NullBooleanField(verbose_name="est disposé à réaliser des expertises pour l'AUF")
240
241 #meta
242 date_creation = models.DateField(auto_now_add=True, db_column='date_creation')
243 date_modification = models.DateField(auto_now=True, db_column='date_modification')
244
245 # Manager
246 objects = ChercheurManager()
247 all_objects = models.Manager()
248
249 def __unicode__(self):
250 return u"%s %s" % (self.nom.upper(), self.prenom.title())
251
252 def statut_display(self):
253 for s in STATUT_CHOICES:
254 if self.statut == s[0]:
255 return s[1]
256 return "-"
257
258 @property
259 def etablissement_display(self):
260 if self.etablissement:
261 return self.etablissement.nom + ', ' + self.etablissement.pays.nom
262 else:
263 return self.etablissement_autre_nom + ', ' + self.etablissement_autre_pays.nom
264
265 @property
266 def pays(self):
267 return self.etablissement.pays if self.etablissement else self.etablissement_autre_pays
268
269 @property
270 def region(self):
271 return self.pays.region
272
273 def save(self):
274 """Si on a donné un établissement membre, on laisse tomber l'autre établissement."""
275 if self.etablissement:
276 self.etablissement_autre_nom = None
277 self.etablissement_autre_pays = None
278 super(Chercheur, self).save()
279
280 def activation_token(self):
281 return sha_constructor(settings.SECRET_KEY + unicode(self.id)).hexdigest()[::2]
282
283 class Publication(models.Model):
284 chercheur = models.ForeignKey(Chercheur, related_name='publications')
285 auteurs = models.CharField(max_length=255, blank=True, verbose_name='auteur(s)')
286 titre = models.CharField(max_length=255, null=True, blank=True, verbose_name='titre')
287 revue = models.CharField(max_length=255, null=True, blank=True, verbose_name='revue')
288 annee = models.IntegerField(null=True, blank=True, verbose_name='année de publication')
289 editeur = models.CharField(max_length=255, null=True, blank=True, verbose_name='éditeur')
290 lieu_edition = models.CharField(max_length=255, null=True, blank=True, verbose_name="lieu d'édition")
291 nb_pages = models.CharField(max_length=255, null=True, blank=True, verbose_name='nombre de pages')
292 url = models.URLField(max_length=255, null=True, blank=True, verbose_name='lien vers la publication', verify_exists=False)
293 #Migration des publications depuis l'ancien repertoire de chercheurs
294 publication_affichage = models.TextField(verbose_name='publication', null=True, blank=True)
295 actif = models.BooleanField(editable=False)
296
297 def __unicode__(self):
298 return self.titre or '(Aucun)'
299
300 def save(self):
301 if self.publication_affichage and (self.auteurs or self.titre or
302 self.revue or self.annee or
303 self.editeur or self.lieu_edition
304 or self.nb_pages or self.url):
305 self.publication_affichage = ''
306 super(Publication, self).save()
307
308 class These(models.Model):
309 chercheur = models.OneToOneField(Chercheur, primary_key=True)
310 titre = models.CharField(max_length=255, verbose_name='Titre')
311 annee = models.IntegerField(verbose_name='Année de soutenance (réalisée ou prévue)')
312 directeur = models.CharField(max_length=255, verbose_name='Directeur')
313 etablissement = models.CharField(max_length=255, verbose_name='Établissement de soutenance')
314 nb_pages = models.IntegerField(verbose_name='Nombre de pages', blank=True, null=True)
315 url = models.URLField(max_length=255, verbose_name='Lien vers la publication', blank=True, verify_exists=False)
316
317 def __unicode__(self):
318 return self.titre
319
320 class Expertise(models.Model):
321 id = models.AutoField(primary_key=True, db_column='id')
322 chercheur = models.ForeignKey(Chercheur, related_name='expertises')
323 nom = models.CharField(max_length=255, verbose_name = "Objet de l'expertise")
324 date = models.CharField(max_length=255, blank=True)
325 lieu = models.CharField(max_length=255, null=True, blank=True, verbose_name = "Lieu de l'expertise")
326 organisme_demandeur = models.CharField(max_length=255, null=True, blank=True, verbose_name = 'Organisme demandeur')
327 organisme_demandeur_visible = models.BooleanField(verbose_name="Afficher l'organisme demandeur")
328 actif = models.BooleanField(editable = False, db_column='actif')
329
330 def __unicode__(self):
331 return u"%s" % (self.nom)
332
333 class Groupe(models.Model):
334 id = models.AutoField(primary_key=True, db_column='id')
335 nom = models.CharField(max_length=255, db_column='nom')
336 url = models.URLField(max_length=255, null=True, blank=True,
337 verbose_name='Site web')
338 liste_diffusion = models.URLField(max_length=255, null=True, blank=True,
339 verbose_name='Liste de diffusion')
340 bulletin = models.URLField(max_length=255, null=True, blank=True,
341 verbose_name='Bulletin')
342 actif = models.BooleanField(editable = False, db_column='actif')
343
344 class Meta:
345 verbose_name = 'domaine de recherche'
346 verbose_name_plural = 'domaines de recherche'
347
348 def __unicode__(self):
349 return u"%s" % (self.nom)
350
351 class ChercheurGroupe(models.Model):
352 id = models.AutoField(primary_key=True, db_column='id')
353 chercheur = models.ForeignKey('Chercheur', db_column='chercheur', editable=False)
354 groupe = models.ForeignKey('Groupe', db_column='groupe')
355 date_inscription = models.DateField(auto_now_add=True)
356 date_modification = models.DateField(auto_now=True)
357 actif = models.BooleanField(editable = False, db_column='actif')
358
359 class Meta:
360 verbose_name = 'adhésion'
361
362 def __unicode__(self):
363 return u"%s - %s" % (self.chercheur, self.groupe)