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