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