762bdc7be89f96f4d44b5e331bd0c383e6071d7d
[auf_rh_dae.git] / project / recrutement / admin.py
1 # -*- encoding: utf-8 -*-
2
3 from django.core.urlresolvers import reverse
4 from django.http import HttpResponseRedirect
5 from django.contrib import admin
6 from django.shortcuts import get_object_or_404
7 from django.core.files.storage import default_storage
8
9 from reversion.admin import VersionAdmin
10 from datamaster_modeles.models import Employe, Implantation, Region
11
12 from recrutement.models import *
13 from recrutement.workflow import grp_administrateurs_recrutement,\
14 grp_evaluateurs_recrutement, grp_drh_recrutement
15
16 class OffreEmploiAdmin(VersionAdmin):
17 date_hierarchy = 'date_creation'
18 list_display = ('nom', 'resume', 'date_limite', 'region', 'statut',
19 'est_affiche', '_candidatsList')
20 list_filter = ('statut', 'est_affiche', )
21 actions = ['affecter_evaluateurs_offre_emploi', ]
22
23 # Affecter un évaluateurs à des offres d'emploi
24 def affecter_evaluateurs_offre_emploi(modeladmin, obj, candidats):
25 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
26
27 return HttpResponseRedirect(reverse('affecter_evaluateurs_offre_emploi')+
28 "?ids=%s" % (",".join(selected)))
29 affecter_evaluateurs_offre_emploi.short_description = u'Affecter évaluateur(s)'
30
31 # Afficher la liste des candidats pour l'offre d'emploi
32 def _candidatsList(self, obj):
33 return "<a href='%s?offre_emploi__id__exact=%s'>Voir les candidats \
34 </a>" % (reverse('admin:recrutement_candidat_changelist'), obj.id)
35 _candidatsList.allow_tags = True
36 _candidatsList.short_description = "Afficher la liste des candidats"
37
38
39 def queryset(self, request):
40 qs = self.model._default_manager.get_query_set()
41 # Si user est superuser afficher toutes les offres d'emploi
42 user_groupes = request.user.groups.all()
43 if not grp_drh_recrutement in user_groupes and \
44 not request.user.is_superuser:
45 """
46 Si le user n'est ni un évaluateur ni un administrateur régional,
47 retourner none
48 Vérifier groupes
49 """
50 if grp_evaluateurs_recrutement in user_groupes:
51 try:
52 user = Evaluateur.objects.get(user=request.user)
53 except Evaluateur.DoesNotExist:
54 return qs.none()
55 elif grp_administrateurs_recrutement in user_groupes:
56 try:
57 user = AdministrateurRegional.objects.get(user=request.user)
58 except AdministrateurRegional.DoesNotExist:
59 return qs.none()
60 else:
61 return qs.none()
62
63 if type(user) is AdministrateurRegional:
64 region_ids = [g.id for g in user.regions.all()]
65 return qs.select_related('offre_emploi').\
66 filter(region__in=region_ids)
67 if type(user) is Evaluateur:
68 candidats = [g for g in user.candidats.all()]
69 offre_emploi_ids = [c.offre_emploi.id for c in candidats]
70 return qs.select_related('offre_emploi').\
71 filter(id__in=offre_emploi_ids)
72 return qs.none()
73 return qs.select_related('offre_emploi')
74
75 def has_change_permission(self, request, obj=None):
76 user_groupes = request.user.groups.all()
77 if grp_drh_recrutement in user_groupes or \
78 grp_administrateurs_recrutement in user_groupes or \
79 request.user.is_superuser:
80 return True
81 return False
82
83 class ProxyOffreEmploiAdmin(OffreEmploiAdmin):
84 list_display = ('nom', 'resume', 'date_limite', 'region', 'statut',
85 'est_affiche')
86 readonly_fields = ('description', 'bureau',
87 'duree_affectation', 'renumeration',
88 'debut_affectation', 'lieu_affectation', 'nom',
89 'resume', 'date_limite', 'region')
90 fieldsets = (
91 ('Nom', {
92 'fields': ('nom', )
93 }),
94 ('Description générale', {
95 'fields': ('resume','description', 'date_limite', )
96 }),
97 ('Coordonnées', {
98 'fields': ('lieu_affectation', 'bureau', 'region', )
99 }),
100 ('Autre', {
101 'fields': ('debut_affectation', 'duree_affectation',
102 'renumeration', )
103 }),
104 )
105 def has_add_permission(self, request):
106 return False
107
108 def has_delete_permission(self, request, obj=None):
109 return False
110
111 def has_change_permission(self, request, obj=None):
112 user_groupes = request.user.groups.all()
113 if grp_evaluateurs_recrutement in user_groupes or \
114 grp_drh_recrutement in user_groupes or \
115 request.user.is_superuser:
116 return True
117 return False
118
119 class ProxyCandidatPiece(CandidatPiece):
120 class Meta:
121 proxy = True
122 verbose_name = "pièce jointe"
123 verbose_name_plural = "pièces jointes"
124
125 class CandidatPieceInline(admin.TabularInline):
126 model = ProxyCandidatPiece
127 fields = ('candidat', 'nom', 'path',)
128 extra = 1
129 max_num = 3
130
131 class ProxyEvaluateur(Evaluateur.candidats.through):
132 """
133 Ce proxy sert uniquement dans l'admin à disposer d'un libellé
134 plus ergonomique.
135 """
136 class Meta:
137 proxy = True
138 verbose_name = "évaluateur"
139
140 class EvaluateurInline(admin.TabularInline):
141 model = ProxyEvaluateur
142 fields = ('evaluateur',)
143 extra = 1
144
145 class CandidatAdmin(VersionAdmin):
146 date_hierarchy = 'date_creation'
147 list_display = ('nom', 'prenom', 'offre_emploi','statut',
148 'voir_offre_emploi', 'calculer_moyenne',
149 'afficher_candidat',)
150 list_filter = ('offre_emploi', )
151 fieldsets = (
152 ("Offre d'emploi", {
153 'fields': ('offre_emploi', )
154 }),
155 ('Informations personnelles', {
156 'fields': ('prenom','nom','genre', 'nationalite',
157 'situation_famille', 'nombre_dependant',)
158 }),
159 ('Coordonnées', {
160 'fields': ('telephone', 'email', 'adresse', 'ville',
161 'etat_province', 'code_postal', 'pays', )
162 }),
163 ('Informations professionnelles', {
164 'fields': ('niveau_diplome','employeur_actuel',
165 'poste_actuel', 'domaine_professionnel',)
166 }),
167 ('Traitement', {
168 'fields': ('statut', )
169 }),
170 )
171 inlines = [
172 CandidatPieceInline,
173 EvaluateurInline,
174 ]
175
176 actions = ['affecter_candidats_evaluateur', 'envoyer_courriel_candidats']
177 # Affecter un évaluateurs à des candidats
178 def affecter_candidats_evaluateur(modeladmin, obj, candidats):
179 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
180
181 return HttpResponseRedirect(reverse('affecter_evaluateurs_candidats')+
182 "?ids=%s" % (",".join(selected)))
183 affecter_candidats_evaluateur.short_description = u'Affecter évaluateur(s)'
184
185 # Envoyer un courriel à des candidats
186 def envoyer_courriel_candidats(modeladmin, obj, candidats):
187 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
188
189 return HttpResponseRedirect(reverse('selectionner_template')+
190 "?ids=%s" % (",".join(selected)))
191 envoyer_courriel_candidats.short_description = u'Envoyer courriel'
192
193 # Évaluer un candidat
194 def evaluer_candidat(self, obj):
195 return "<a href='%s?candidat__id__exact=%s'>Évaluer le candidat</a>" % \
196 (reverse('admin:recrutement_candidatevaluation_changelist'),
197 obj.id)
198 evaluer_candidat.allow_tags = True
199 evaluer_candidat.short_description = 'Évaluation'
200
201 # Afficher un candidat
202 def afficher_candidat(self, obj):
203 return "<a href='%s'>Voir le candidat</a>" % \
204 (reverse('admin:recrutement_proxycandidat_change', args=(obj.id,)))
205 afficher_candidat.allow_tags = True
206 afficher_candidat.short_description = u'Détails du candidat'
207
208 # Voir l'offre d'emploi
209 def voir_offre_emploi(self, obj):
210 return "<a href='%s'>Voir l'offre d'emploi</a>" % \
211 (reverse('admin:recrutement_proxyoffreemploi_change',
212 args=(obj.offre_emploi.id,)))
213 voir_offre_emploi.allow_tags = True
214 voir_offre_emploi.short_description = "Afficher l'offre d'emploi"
215
216 # Calculer la moyenne des notes
217 def calculer_moyenne(self, obj):
218 evaluations = CandidatEvaluation.objects.filter(candidat=obj)
219 offre_emploi = obj.offre_emploi
220
221 notes = [evaluation.note for evaluation in evaluations.all() \
222 if evaluation.note is not None]
223
224 if len(notes) > 0 and offre_emploi.date_limite <= datetime.date.today():
225 moyenne_votes = float(sum(notes)) / len(notes)
226 else:
227 moyenne_votes = "Non disponible"
228 return moyenne_votes
229 calculer_moyenne.allow_tags = True
230 calculer_moyenne.short_description = "Moyenne des notes"
231
232 def add_delete_permission(self, request, obj=None) :
233 user_groupes = request.user.groups.all()
234 if grp_drh_recrutement in user_groupes or \
235 grp_administrateurs_recrutement in user_groupes or \
236 request.user.is_superuser:
237 return True
238 return False
239
240 def has_add_permission(self, request):
241 return self.add_delete_permission(request, request)
242
243 def has_delete_permission(self, request, obj=None):
244 return self.add_delete_permission(request, request)
245
246 def has_change_permission(self, request, obj=None):
247 user_groupes = request.user.groups.all()
248 if grp_drh_recrutement in user_groupes or \
249 grp_administrateurs_recrutement in user_groupes or \
250 request.user.is_superuser:
251 return True
252 return False
253
254 def queryset(self, request):
255 """
256 Spécifie un queryset limité, autrement Django exécute un
257 select_related() sans paramètre, ce qui a pour effet de charger tous
258 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
259 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
260 génération d'une requête infinie.
261
262 """
263 qs = self.model._default_manager.get_query_set()
264 # Si user est superuser afficher tous les candidats
265 user_groupes = request.user.groups.all()
266 if not grp_drh_recrutement in user_groupes and \
267 not request.user.is_superuser:
268 # Si le user n'est ni un évaluateur ni un administrateur régional,
269 # retourner none
270
271 # Vérifier groupes
272 if grp_evaluateurs_recrutement in user_groupes:
273 try:
274 user = Evaluateur.objects.get(user=request.user)
275 except Evaluateur.DoesNotExist:
276 return qs.none()
277 """
278 elif grp_administrateurs_recrutement in user_groupes:
279 try:
280 user = AdministrateurRegional.objects.get(user=obj.user)
281 except AdministrateurRegional.DoesNotExist:
282 return qs.none()
283 """
284 else:
285 return qs.none()
286 ids = [c.id for c in user.candidats.all()]
287 return qs.select_related('candidats').filter(id__in=ids)
288 return qs.select_related('candidats')
289
290 class ProxyCandidatAdmin(CandidatAdmin):
291 readonly_fields = ('statut', 'offre_emploi', 'prenom', 'nom',
292 'genre', 'nationalite', 'situation_famille',
293 'nombre_dependant', 'telephone', 'email', 'adresse',
294 'ville', 'etat_province', 'code_postal', 'pays',
295 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
296 'domaine_professionnel', 'pieces_jointes',)
297 fieldsets = (
298 ("Offre d'emploi", {
299 'fields': ('offre_emploi', )
300 }),
301 ('Informations personnelles', {
302 'fields': ('prenom','nom','genre', 'nationalite',
303 'situation_famille', 'nombre_dependant',)
304 }),
305 ('Coordonnées', {
306 'fields': ('telephone', 'email', 'adresse', 'ville',
307 'etat_province', 'code_postal', 'pays', )
308 }),
309 ('Informations professionnelles', {
310 'fields': ('niveau_diplome','employeur_actuel',
311 'poste_actuel', 'domaine_professionnel',)
312 }),
313 )
314 inlines = []
315
316 def has_add_permission(self, request):
317 return False
318
319 def has_delete_permission(self, request, obj=None):
320 return False
321
322 def has_change_permission(self, request, obj=None):
323 user_groupes = request.user.groups.all()
324 if grp_drh_recrutement in user_groupes or \
325 grp_administrateurs_recrutement in user_groupes or \
326 grp_evaluateurs_recrutement in user_groupes or \
327 request.user.is_superuser:
328 return True
329 return False
330
331 class CandidatPieceAdmin(admin.ModelAdmin):
332 list_display = ('nom', 'candidat', )
333
334 def queryset(self, request):
335 """
336 Spécifie un queryset limité, autrement Django exécute un
337 select_related() sans paramètre, ce qui a pour effet de charger tous
338 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
339 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
340 génération d'une requête infinie.
341 Affiche la liste de candidats que si le user connecté
342 possède un Evaluateur
343 """
344 qs = self.model._default_manager.get_query_set()
345 return qs.select_related('candidat')
346
347 class EvaluateurAdmin(VersionAdmin):
348 fieldsets = (
349 (None, {'fields': ('user', )}),
350 #(None, {'fields': ('candidats',)}),
351 )
352
353 class AdministrateurRegionalAdmin(VersionAdmin):
354 pass
355
356 class CandidatEvaluationAdmin(VersionAdmin):
357 list_display = ('_candidat', '_offre_emploi', 'evaluateur', '_note',
358 '_commentaire', )
359 readonly_fields = ('candidat', 'evaluateur')
360 fieldsets = (
361 ('Évaluation du candidat', {
362 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
363 }),
364 )
365
366 def _note(self, obj):
367 """
368 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
369 un lien pour Évaluer le candidat.
370 Sinon afficher la note.
371 """
372 evaluateur = obj.evaluateur
373 candidat = obj.candidat
374 candidat_evaluation = CandidatEvaluation.objects.\
375 get(candidat=candidat, evaluateur=evaluateur)
376 if obj.note is None:
377 return "<a href='%s'>Candidat non évalué</a>" % \
378 (reverse('admin:recrutement_candidatevaluation_change',
379 args=(candidat_evaluation.id,)))
380 return "<a href='%s'>%s</a>" % \
381 (reverse('admin:recrutement_candidatevaluation_change',
382 args=(candidat_evaluation.id,)), obj.note)
383 return
384 _note.allow_tags = True
385 _note.short_description = "Votre note"
386 _note.admin_order_field = 'note'
387
388 def _candidat(self, obj):
389 return "<a href='%s'>%s</a>" \
390 % (reverse('admin:recrutement_proxycandidat_change',
391 args=(obj.candidat.id,)), obj.candidat)
392 _candidat.allow_tags = True
393 _candidat.short_description = 'Candidat'
394
395 def _commentaire(self, obj):
396 """
397 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
398 dans le champ commentaire, Aucun au lieu de (None)
399 Sinon afficher la note.
400 """
401 if obj.commentaire is None:
402 return "Aucun"
403 return obj.commentaire
404 _commentaire.allow_tags = True
405 _commentaire.short_description = "Commentaire"
406
407
408 def _offre_emploi(self, obj):
409 return "<a href='%s'>%s</a>" % \
410 (reverse('admin:recrutement_proxyoffreemploi_change',
411 args=(obj.candidat.offre_emploi.id,)), obj.candidat.offre_emploi)
412 _offre_emploi.allow_tags = True
413 _offre_emploi.short_description = "Voir offre d'emploi"
414
415 def has_change_permission(self, request, obj=None):
416 """
417 Permettre la visualisation dans la changelist
418 mais interdire l'accès à modifier l'objet si l'évaluateur n'est pas
419 le request.user
420 """
421 return obj is None or request.user == obj.evaluateur.user
422
423 def queryset(self, request):
424 """
425 Afficher uniquement les évaluations de l'évaluateur, sauf si
426 l'utilisateur est super admin.
427 """
428 qs = self.model._default_manager.get_query_set()
429 user_groupes = request.user.groups.all()
430 if grp_drh_recrutement in user_groupes or \
431 request.user.is_superuser:
432 return qs.select_related('offre_emploi')
433
434 try:
435 evaluateur = Evaluateur.objects.get(user=request.user)
436 except Evaluateur.DoesNotExist:
437 return qs.none()
438
439 candidats_evaluations = CandidatEvaluation.objects.\
440 filter(evaluateur=evaluateur)
441 candidats_evaluations_ids = [ce.id for ce in \
442 candidats_evaluations.all()]
443 return qs.select_related('offre_emploi').\
444 filter(id__in=candidats_evaluations_ids)
445
446 class CourrielTemplateAdmin(VersionAdmin):
447 pass
448
449 admin.site.register(OffreEmploi, OffreEmploiAdmin)
450 admin.site.register(ProxyOffreEmploi, ProxyOffreEmploiAdmin)
451 admin.site.register(Candidat, CandidatAdmin)
452 admin.site.register(ProxyCandidat, ProxyCandidatAdmin)
453 admin.site.register(CandidatEvaluation, CandidatEvaluationAdmin)
454 admin.site.register(Evaluateur, EvaluateurAdmin)
455 #admin.site.register(AdministrateurRegional, AdministrateurRegionalAdmin)
456 admin.site.register(CourrielTemplate, CourrielTemplateAdmin)