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