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