1777: Revenir à la liste des candidats ou offre d'emploi, au lieu des liste de visual...
[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
df59fcab 16
a029f641
NBV
17"""
18class MetaAdmin(VersionAdmin):
19 def get_actions(self, request):
20
21Pour refactoring
22"""
d2b30f5f 23class OffreEmploiAdmin(VersionAdmin):
7f9e891e 24 date_hierarchy = 'date_creation'
4fd0c4a2
NBV
25 list_display = ('nom', 'resume', 'date_limite', 'region', 'statut',
26 'est_affiche', '_candidatsList')
514db699 27 list_filter = ('statut', 'est_affiche', )
c4874d66 28 actions = ['affecter_evaluateurs_offre_emploi', ]
2c3c54ee 29
a029f641
NBV
30 def get_actions(self, request):
31 actions = super(OffreEmploiAdmin, self).get_actions(request)
32 del actions['delete_selected']
33
c4874d66
NBV
34 # Affecter un évaluateurs à des offres d'emploi
35 def affecter_evaluateurs_offre_emploi(modeladmin, obj, candidats):
36 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
37
38 return HttpResponseRedirect(reverse('affecter_evaluateurs_offre_emploi')+
39 "?ids=%s" % (",".join(selected)))
40 affecter_evaluateurs_offre_emploi.short_description = u'Affecter évaluateur(s)'
41
8941aee7 42 # Afficher la liste des candidats pour l'offre d'emploi
596fe324 43 def _candidatsList(self, obj):
8ea41642 44 return "<a href='%s?offre_emploi__id__exact=%s'>Voir les candidats \
45 </a>" % (reverse('admin:recrutement_candidat_changelist'), obj.id)
2f78949d 46 _candidatsList.allow_tags = True
f6724c20 47 _candidatsList.short_description = "Afficher la liste des candidats"
362a3534 48
c4874d66 49
2f78949d 50 def queryset(self, request):
382501c1 51 qs = self.model._default_manager.get_query_set()
f6724c20
NBV
52 # Si user est superuser afficher toutes les offres d'emploi
53 user_groupes = request.user.groups.all()
d46075cb
NBV
54 if not grp_drh_recrutement in user_groupes and \
55 not request.user.is_superuser:
beef7690 56 """
beef7690
NBV
57 Si le user n'est ni un évaluateur ni un administrateur régional,
58 retourner none
59 Vérifier groupes
60 """
f6724c20 61 if grp_evaluateurs_recrutement in user_groupes:
ca73c3c6
NBV
62 try:
63 user = Evaluateur.objects.get(user=request.user)
64 except Evaluateur.DoesNotExist:
65 return qs.none()
f6724c20 66 elif grp_administrateurs_recrutement in user_groupes:
ca73c3c6
NBV
67 try:
68 user = AdministrateurRegional.objects.get(user=request.user)
69 except AdministrateurRegional.DoesNotExist:
70 return qs.none()
f6724c20
NBV
71 else:
72 return qs.none()
73
74 if type(user) is AdministrateurRegional:
75 region_ids = [g.id for g in user.regions.all()]
76 return qs.select_related('offre_emploi').\
77 filter(region__in=region_ids)
beef7690 78 if type(user) is Evaluateur:
f6724c20 79 candidats = [g for g in user.candidats.all()]
beef7690 80 offre_emploi_ids = [c.offre_emploi.id for c in candidats]
f6724c20 81 return qs.select_related('offre_emploi').\
beef7690 82 filter(id__in=offre_emploi_ids)
3bcef02d 83 return qs.none()
382501c1 84 return qs.select_related('offre_emploi')
f6724c20
NBV
85
86 def has_change_permission(self, request, obj=None):
87 user_groupes = request.user.groups.all()
88 if grp_drh_recrutement in user_groupes or \
d46075cb
NBV
89 grp_administrateurs_recrutement in user_groupes or \
90 request.user.is_superuser:
f6724c20
NBV
91 return True
92 return False
93
94class ProxyOffreEmploiAdmin(OffreEmploiAdmin):
4fd0c4a2
NBV
95 list_display = ('nom', 'resume', 'date_limite', 'region', 'statut',
96 'est_affiche')
14e06ff6 97 readonly_fields = ('description', 'bureau',
f6724c20
NBV
98 'duree_affectation', 'renumeration',
99 'debut_affectation', 'lieu_affectation', 'nom',
100 'resume', 'date_limite', 'region')
101 fieldsets = (
720c3ad5 102 ('Nom', {
f6724c20
NBV
103 'fields': ('nom', )
104 }),
720c3ad5 105 ('Description générale', {
14e06ff6 106 'fields': ('resume','description', 'date_limite', )
f6724c20 107 }),
720c3ad5 108 ('Coordonnées', {
f6724c20
NBV
109 'fields': ('lieu_affectation', 'bureau', 'region', )
110 }),
720c3ad5 111 ('Autre', {
f6724c20
NBV
112 'fields': ('debut_affectation', 'duree_affectation',
113 'renumeration', )
114 }),
115 )
a029f641
NBV
116
117 def response_change(self, request, obj):
118 response = super(ProxyOffreEmploiAdmin, self).response_change(request, obj)
119 user_groupes = request.user.groups.all()
120 if grp_drh_recrutement in user_groupes or \
121 request.user.is_superuser:
122 return HttpResponseRedirect(reverse('admin:recrutement_offreemploi_changelist'))
123 return HttpResponseRedirect(reverse('admin:recrutement_proxyoffreemploi_changelist'))
124
f6724c20
NBV
125 def has_add_permission(self, request):
126 return False
127
128 def has_delete_permission(self, request, obj=None):
129 return False
130
2d083449
NBV
131 def has_change_permission(self, request, obj=None):
132 user_groupes = request.user.groups.all()
beef7690 133 if grp_evaluateurs_recrutement in user_groupes or \
d46075cb
NBV
134 grp_drh_recrutement in user_groupes or \
135 request.user.is_superuser:
2d083449
NBV
136 return True
137 return False
138
27c81d11 139class ProxyEvaluateur(Evaluateur.candidats.through):
cced6a23 140 """
141 Ce proxy sert uniquement dans l'admin à disposer d'un libellé
142 plus ergonomique.
143 """
144 class Meta:
145 proxy = True
146 verbose_name = "évaluateur"
147
572c8d08
NBV
148class CandidatPieceInline(admin.TabularInline):
149 model = CandidatPiece
150 fields = ('candidat', 'nom', 'path',)
151 extra = 1
152 max_num = 3
153
eb579d40 154class EvaluateurInline(admin.TabularInline):
cced6a23 155 model = ProxyEvaluateur
720c3ad5 156 fields = ('evaluateur',)
eb579d40 157 extra = 1
158
572c8d08
NBV
159
160class CandidatEvaluationInlineFormSet(BaseInlineFormSet):
161 """
162 Empêche la suppression d'une évaluation pour le CandidatEvaluationInline
163 """
164 def __init__(self, *args, **kwargs):
165 super(CandidatEvaluationInlineFormSet, self).__init__(*args, **kwargs)
166 self.can_delete = False
167
168class CandidatEvaluationInline(admin.TabularInline):
169 model = CandidatEvaluation
170 fields = ('evaluateur', 'note', 'commentaire')
171 max_num = 0
172 extra = 0
173 formset = CandidatEvaluationInlineFormSet
174
175 def get_readonly_fields(self, request, obj=None):
176 """
177 Empêche la modification des évaluations
178 """
179 if obj:
180 return self.readonly_fields+('evaluateur', 'note', 'commentaire')
181 return self.readonly_fields
182
183
d2b30f5f 184class CandidatAdmin(VersionAdmin):
7f9e891e 185 date_hierarchy = 'date_creation'
0fd8a26d 186 list_display = ('nom', 'prenom', 'offre_emploi','statut',
dc7faf2b
NBV
187 'voir_offre_emploi', 'calculer_moyenne',
188 'afficher_candidat',)
8ea41642 189 list_filter = ('offre_emploi', )
7f9e891e 190 fieldsets = (
4896b661 191 ("Offre d'emploi", {
192 'fields': ('offre_emploi', )
193 }),
7f9e891e 194 ('Informations personnelles', {
4fd0c4a2 195 'fields': ('prenom','nom','genre', 'nationalite',
7f9e891e 196 'situation_famille', 'nombre_dependant',)
197 }),
ec517164 198 ('Coordonnées', {
199 'fields': ('telephone', 'email', 'adresse', 'ville',
200 'etat_province', 'code_postal', 'pays', )
7f9e891e 201 }),
202 ('Informations professionnelles', {
4896b661 203 'fields': ('niveau_diplome','employeur_actuel',
8ea41642 204 'poste_actuel', 'domaine_professionnel',)
7f9e891e 205 }),
65c4cbd9
NBV
206 ('Traitement', {
207 'fields': ('statut', )
7f9e891e 208 }),
209 )
170c9aa2 210 inlines = [
211 CandidatPieceInline,
eb579d40 212 EvaluateurInline,
572c8d08 213 CandidatEvaluationInline,
170c9aa2 214 ]
f9983b5a 215
4e8340cf 216 actions = ['affecter_candidats_evaluateur', 'envoyer_courriel_candidats']
a029f641
NBV
217
218 def get_actions(self, request):
219 actions = super(CandidatAdmin, self).get_actions(request)
220 del actions['delete_selected']
221
362a3534 222 # Affecter un évaluateurs à des candidats
7061f835 223 def affecter_candidats_evaluateur(modeladmin, obj, candidats):
596fe324 224 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
2adf9e0c 225
8ea41642 226 return HttpResponseRedirect(reverse('affecter_evaluateurs_candidats')+
227 "?ids=%s" % (",".join(selected)))
c4874d66 228 affecter_candidats_evaluateur.short_description = u'Affecter évaluateur(s)'
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 = (
f6724c20 402 (None, {'fields': ('user', )}),
720c3ad5 403 #(None, {'fields': ('candidats',)}),
eb579d40 404 )
4418c732 405
a029f641
NBV
406 def get_actions(self, request):
407 actions = super(EvaluateurAdmin, self).get_actions(request)
408 del actions['delete_selected']
409
b89fef74 410class AdministrateurRegionalAdmin(VersionAdmin):
27c81d11 411 pass
412
d2b30f5f 413class CandidatEvaluationAdmin(VersionAdmin):
2c3c54ee 414 list_display = ('_candidat', '_offre_emploi', 'evaluateur', '_note',
b903198b 415 '_commentaire', )
beef7690
NBV
416 readonly_fields = ('candidat', 'evaluateur')
417 fieldsets = (
418 ('Évaluation du candidat', {
419 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
420 }),
421 )
422
a029f641
NBV
423 def get_actions(self, request):
424 actions = super(CandidatEvaluationAdmin, self).get_actions(request)
425 del actions['delete_selected']
426
beef7690
NBV
427 def _note(self, obj):
428 """
429 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
430 un lien pour Évaluer le candidat.
431 Sinon afficher la note.
432 """
b903198b
NBV
433 evaluateur = obj.evaluateur
434 candidat = obj.candidat
435 candidat_evaluation = CandidatEvaluation.objects.\
436 get(candidat=candidat, evaluateur=evaluateur)
beef7690 437 if obj.note is None:
b903198b 438 return "<a href='%s'>Candidat non évalué</a>" % \
beef7690 439 (reverse('admin:recrutement_candidatevaluation_change',
b903198b
NBV
440 args=(candidat_evaluation.id,)))
441 return "<a href='%s'>%s</a>" % \
442 (reverse('admin:recrutement_candidatevaluation_change',
443 args=(candidat_evaluation.id,)), obj.note)
444 return
beef7690
NBV
445 _note.allow_tags = True
446 _note.short_description = "Votre note"
447 _note.admin_order_field = 'note'
448
449 def _candidat(self, obj):
450 return "<a href='%s'>%s</a>" \
451 % (reverse('admin:recrutement_proxycandidat_change',
452 args=(obj.candidat.id,)), obj.candidat)
453 _candidat.allow_tags = True
454 _candidat.short_description = 'Candidat'
455
456 def _commentaire(self, obj):
457 """
458 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
459 dans le champ commentaire, Aucun au lieu de (None)
460 Sinon afficher la note.
461 """
462 if obj.commentaire is None:
463 return "Aucun"
464 return obj.commentaire
465 _commentaire.allow_tags = True
466 _commentaire.short_description = "Commentaire"
720c3ad5 467
720c3ad5 468
beef7690
NBV
469 def _offre_emploi(self, obj):
470 return "<a href='%s'>%s</a>" % \
471 (reverse('admin:recrutement_proxyoffreemploi_change',
dc7faf2b 472 args=(obj.candidat.offre_emploi.id,)), obj.candidat.offre_emploi)
beef7690
NBV
473 _offre_emploi.allow_tags = True
474 _offre_emploi.short_description = "Voir offre d'emploi"
beef7690 475
21b02da5
NBV
476 def has_change_permission(self, request, obj=None):
477 """
478 Permettre la visualisation dans la changelist
479 mais interdire l'accès à modifier l'objet si l'évaluateur n'est pas
480 le request.user
481 """
482 return obj is None or request.user == obj.evaluateur.user
483
720c3ad5 484 def queryset(self, request):
beef7690
NBV
485 """
486 Afficher uniquement les évaluations de l'évaluateur, sauf si
487 l'utilisateur est super admin.
488 """
720c3ad5 489 qs = self.model._default_manager.get_query_set()
beef7690 490 user_groupes = request.user.groups.all()
d46075cb
NBV
491 if grp_drh_recrutement in user_groupes or \
492 request.user.is_superuser:
beef7690
NBV
493 return qs.select_related('offre_emploi')
494
ca73c3c6 495 try:
d069cdf1 496 evaluateur = Evaluateur.objects.get(user=request.user)
ca73c3c6
NBV
497 except Evaluateur.DoesNotExist:
498 return qs.none()
499
500 candidats_evaluations = CandidatEvaluation.objects.\
501 filter(evaluateur=evaluateur)
502 candidats_evaluations_ids = [ce.id for ce in \
503 candidats_evaluations.all()]
504 return qs.select_related('offre_emploi').\
505 filter(id__in=candidats_evaluations_ids)
596fe324 506
4e8340cf 507class CourrielTemplateAdmin(VersionAdmin):
a029f641
NBV
508 def get_actions(self, request):
509 actions = super(CourrielTemplateAdmin, self).get_actions(request)
510 del actions['delete_selected']
4e8340cf 511
df59fcab 512admin.site.register(OffreEmploi, OffreEmploiAdmin)
382501c1 513admin.site.register(ProxyOffreEmploi, ProxyOffreEmploiAdmin)
df59fcab 514admin.site.register(Candidat, CandidatAdmin)
382501c1 515admin.site.register(ProxyCandidat, ProxyCandidatAdmin)
720c3ad5 516admin.site.register(CandidatEvaluation, CandidatEvaluationAdmin)
27c81d11 517admin.site.register(Evaluateur, EvaluateurAdmin)
beef7690 518#admin.site.register(AdministrateurRegional, AdministrateurRegionalAdmin)
32834000 519admin.site.register(CourrielTemplate, CourrielTemplateAdmin)