[#2034] Remonter la date de début de l'affectation pour la mettre en dessous du bureau.
[auf_rh_dae.git] / project / recrutement / admin.py
1 # -*- encoding: utf-8 -*-
2
3 import textwrap
4
5 from django.conf import settings
6 from django.contrib import admin
7 from django.core.urlresolvers import reverse
8 from django.db.models import Avg
9 from django.shortcuts import render_to_response
10 from django.template import RequestContext
11
12 from auf.django.emploi.models import STATUT_CHOICES
13 from django.forms.models import BaseInlineFormSet
14 from django.http import HttpResponseRedirect
15 from django.shortcuts import redirect
16 from reversion.admin import VersionAdmin
17
18 from auf.django.emploi.models import OffreEmploi, Candidat, CandidatPiece
19 from auf.django.references.models import Region, Bureau, Implantation
20 from auf.django.export.admin import ExportAdmin
21
22 from project.groups import get_employe_from_user as get_emp
23 from project.rh import models as rh
24
25 from project.recrutement.forms import OffreEmploiForm
26 from project.recrutement.groups import \
27 grp_drh, grp_drh2, \
28 grp_directeurs_bureau, \
29 grp_administrateurs, \
30 grp_correspondants_rh, \
31 grp_haute_direction
32 from project.recrutement.models import \
33 Evaluateur, CandidatEvaluation, \
34 ProxyOffreEmploi, ProxyCandidat, MesCandidatEvaluation, \
35 CourrielTemplate
36
37
38 ### CONSTANTES
39 IMPLANTATIONS_CENTRALES = [15, 19]
40
41
42 class BaseAdmin(admin.ModelAdmin):
43
44 class Media:
45 css = {'screen': ('css/admin_custom.css',)}
46
47
48 class OrderedChangeList(admin.views.main.ChangeList):
49 """
50 Surcharge pour appliquer le order_by d'un annotate
51 """
52 def get_query_set(self):
53 qs = super(OrderedChangeList, self).get_query_set()
54 qs = qs.order_by('-moyenne')
55 return qs
56
57
58 class OffreEmploiAdmin(BaseAdmin, VersionAdmin):
59 date_hierarchy = 'date_creation'
60 list_display = (
61 'nom', 'date_limite', 'region', 'statut', 'est_affiche',
62 '_candidatsList'
63 )
64 exclude = ('actif', 'poste_nom', 'resume',)
65 list_filter = ('statut',)
66 actions = ['affecter_evaluateurs_offre_emploi', ]
67 form = OffreEmploiForm
68 fieldsets = (
69 (None, {
70 'fields': (
71 'est_affiche',
72 'statut',
73 'date_limite',
74 'nom',
75 'description',
76 'poste',
77 'region',
78 'bureau',
79 'debut_affectation',
80 'duree_affectation',
81 'renumeration',
82 'lieu_affectation',
83 )
84 }),
85 )
86
87 ### Actions à afficher
88 def get_actions(self, request):
89 actions = super(OffreEmploiAdmin, self).get_actions(request)
90 del actions['delete_selected']
91 return actions
92
93 ### Affecter un évaluateurs à des offres d'emploi
94 def affecter_evaluateurs_offre_emploi(modeladmin, obj, candidats):
95 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
96
97 return HttpResponseRedirect(
98 reverse('affecter_evaluateurs_offre_emploi') +
99 "?ids=%s" % (",".join(selected))
100 )
101
102 affecter_evaluateurs_offre_emploi.short_description = \
103 u'Affecter évaluateur(s)'
104
105 ### Afficher la liste des candidats pour l'offre d'emploi
106 def _candidatsList(self, obj):
107 return "<a href='%s?offre_emploi__id__exact=%s'>Voir les candidats \
108 </a>" % (reverse('admin:emploi_candidat_changelist'), obj.id)
109 _candidatsList.allow_tags = True
110 _candidatsList.short_description = "Afficher la liste des candidats"
111
112 ### Formulaire
113 def get_form(self, request, obj=None, **kwargs):
114 form = super(OffreEmploiAdmin, self).get_form(request, obj, **kwargs)
115 employe = get_emp(request.user)
116 user_groupes = request.user.groups.all()
117
118 # Region
119 if 'region' in form.declared_fields:
120 region_field = form.declared_fields['region']
121 else:
122 region_field = form.base_fields['region']
123
124 if grp_drh in user_groupes or \
125 grp_drh2 in user_groupes or \
126 grp_haute_direction in user_groupes:
127 region_field.queryset = Region.objects.all()
128 else:
129 region_field.queryset = Region.objects.\
130 filter(id=employe.implantation.region.id)
131
132 # Poste
133 if 'poste' in form.declared_fields:
134 poste_field = form.declared_fields['poste']
135 else:
136 poste_field = form.base_fields['poste']
137
138 if grp_drh in user_groupes or \
139 grp_drh2 in user_groupes or \
140 grp_haute_direction in user_groupes:
141 poste_field.queryset = rh.Poste.objects.all()
142 else:
143 poste_field.queryset = rh.Poste.objects.\
144 filter(implantation__region=employe.implantation.region).\
145 exclude(implantation__in=IMPLANTATIONS_CENTRALES)
146
147 # Bureau
148 if 'bureau' in form.declared_fields:
149 bureau_field = form.declared_fields['bureau']
150 else:
151 bureau_field = form.base_fields['bureau']
152
153 if grp_drh in user_groupes or \
154 grp_drh2 in user_groupes or \
155 grp_haute_direction in user_groupes:
156 bureau_field.queryset = Bureau.objects.all()
157 else:
158 bureau_field.queryset = \
159 Bureau.objects.filter(region=employe.implantation.region)
160
161 return form
162
163 ### Queryset
164
165 def queryset(self, request):
166 qs = self.model._default_manager.get_query_set() \
167 .select_related('offre_emploi')
168 user_groupes = request.user.groups.all()
169 if grp_drh in user_groupes or \
170 grp_drh2 in user_groupes or \
171 grp_haute_direction in user_groupes:
172 return qs
173
174 if grp_directeurs_bureau in user_groupes or \
175 grp_correspondants_rh in user_groupes or \
176 grp_administrateurs in user_groupes:
177 employe = get_emp(request.user)
178 return qs.filter(region=employe.implantation.region)
179
180 if Evaluateur.objects.filter(user=request.user).exists():
181 evaluateur = Evaluateur.objects.get(user=request.user)
182 offre_ids = [
183 e.candidat.offre_emploi_id
184 for e in CandidatEvaluation.objects
185 .select_related('candidat')
186 .filter(evaluateur=evaluateur)
187 ]
188 return qs.filter(id__in=offre_ids)
189
190 return qs.none()
191
192 ### Permission add, delete, change
193 def has_add_permission(self, request):
194 user_groupes = request.user.groups.all()
195 if request.user.is_superuser is True or \
196 grp_drh in user_groupes or \
197 grp_drh2 in user_groupes or \
198 grp_directeurs_bureau in user_groupes or \
199 grp_administrateurs in user_groupes or \
200 grp_haute_direction in user_groupes:
201 return True
202 return False
203
204 def has_delete_permission(self, request, obj=None):
205 user_groupes = request.user.groups.all()
206 if request.user.is_superuser is True or \
207 grp_drh in user_groupes or \
208 grp_drh2 in user_groupes or \
209 grp_directeurs_bureau in user_groupes or \
210 grp_administrateurs in user_groupes or \
211 grp_haute_direction in user_groupes:
212 return True
213 return False
214
215 def has_change_permission(self, request, obj=None):
216 user_groupes = request.user.groups.all()
217 if request.user.is_superuser is True or \
218 grp_drh in user_groupes or \
219 grp_drh2 in user_groupes or \
220 grp_directeurs_bureau in user_groupes or \
221 grp_administrateurs in user_groupes or \
222 grp_haute_direction in user_groupes:
223 return True
224 return False
225
226 def formfield_for_foreignkey(self, db_field, request, **kwargs):
227 if db_field.name == 'lieu_affectation':
228 employe = get_emp(request.user)
229 kwargs["queryset"] = Implantation.objects.filter(region=employe.implantation.region)
230 return db_field.formfield(**kwargs)
231 return super(OffreEmploiAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
232
233
234 class ProxyOffreEmploiAdmin(OffreEmploiAdmin):
235 list_display = (
236 'nom', 'date_limite', 'region', 'statut', 'est_affiche'
237 )
238 readonly_fields = (
239 'description', 'bureau', 'duree_affectation', 'renumeration',
240 'debut_affectation', 'lieu_affectation', 'nom', 'resume',
241 'date_limite', 'region', 'poste'
242 )
243 fieldsets = (
244 ('Nom', {
245 'fields': ('nom',)
246 }),
247 ('Description générale', {
248 'fields': ('description', 'date_limite',)
249 }),
250 ('Coordonnées', {
251 'fields': ('lieu_affectation', 'bureau', 'region', 'poste',)
252 }),
253 ('Autre', {
254 'fields': (
255 'debut_affectation', 'duree_affectation', 'renumeration',
256 )
257 }),
258 )
259 inlines = []
260
261 ### Lieu de redirection après le change
262 def response_change(self, request, obj):
263 return redirect('admin:recrutement_proxyoffreemploi_changelist')
264
265 ### Formulaire
266 def get_form(self, request, obj=None, **kwargs):
267 form = super(OffreEmploiAdmin, self).get_form(request, obj, **kwargs)
268 return form
269
270 ### Permissions add, delete, change
271 def has_add_permission(self, request):
272 return False
273
274 def has_delete_permission(self, request, obj=None):
275 return False
276
277 def has_change_permission(self, request, obj=None):
278 if obj is not None:
279 return True
280
281 return not super(ProxyOffreEmploiAdmin, self).has_change_permission(request, obj)
282
283
284 class CandidatPieceInline(admin.TabularInline):
285 model = CandidatPiece
286 fields = ('candidat', 'nom', 'path',)
287 extra = 1
288 max_num = 3
289
290
291 class ReadOnlyCandidatPieceInline(CandidatPieceInline):
292 readonly_fields = ('candidat', 'nom', 'path', )
293 cand_delete = False
294
295
296 class CandidatEvaluationInlineFormSet(BaseInlineFormSet):
297 """
298 Empêche la suppression d'une évaluation pour le CandidatEvaluationInline
299 """
300 def __init__(self, *args, **kwargs):
301 super(CandidatEvaluationInlineFormSet, self).__init__(*args, **kwargs)
302 self.can_delete = False
303
304
305 class CandidatEvaluationInline(admin.TabularInline):
306 model = CandidatEvaluation
307 fields = ('evaluateur', 'note', 'commentaire')
308 max_num = 0
309 extra = 0
310 formset = CandidatEvaluationInlineFormSet
311
312 ### Fields readonly
313 def get_readonly_fields(self, request, obj=None):
314 """
315 Empêche la modification des évaluations
316 """
317 if obj:
318 return self.readonly_fields + ('evaluateur', 'note', 'commentaire')
319 return self.readonly_fields
320
321 class CandidatAdmin(BaseAdmin, VersionAdmin, ExportAdmin):
322 change_list_template = 'admin/recrutement/candidat/change_list.html'
323 search_fields = ('nom', 'prenom')
324 exclude = ('actif', )
325 list_editable = ('statut', )
326 list_display = ('_candidat', 'offre_emploi',
327 'voir_offre_emploi', 'calculer_moyenne',
328 'afficher_candidat', '_date_creation', 'statut', )
329 list_filter = ('offre_emploi__nom', 'offre_emploi__region', 'statut', )
330
331 fieldsets = (
332 ("Offre d'emploi", {
333 'fields': ('offre_emploi', )
334 }),
335 ('Informations personnelles', {
336 'fields': (
337 'nom', 'prenom', 'genre', 'nationalite',
338 'situation_famille', 'nombre_dependant'
339 )
340 }),
341 ('Coordonnées', {
342 'fields': (
343 'telephone', 'email', 'adresse', 'ville', 'etat_province',
344 'code_postal', 'pays'
345 )
346 }),
347 ('Informations professionnelles', {
348 'fields': (
349 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
350 'domaine_professionnel'
351 )
352 }),
353 ('Traitement', {
354 'fields': ('statut', )
355 }),
356 )
357 inlines = [
358 CandidatPieceInline,
359 CandidatEvaluationInline,
360 ]
361 actions = ['envoyer_courriel_candidats', 'changer_statut']
362
363 export_fields = ['statut', 'offre_emploi', 'prenom', 'nom', 'genre',
364 'nationalite', 'situation_famille', 'nombre_dependant',
365 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
366 'domaine_professionnel', 'telephone', 'email', 'adresse',
367 'ville', 'etat_province', 'code_postal', 'pays']
368
369 def _candidat(self, obj):
370 txt = u"%s %s (%s)" % (obj.nom.upper(), obj.prenom, obj.genre)
371 txt = textwrap.wrap(txt, 30)
372 return "<br/>".join(txt)
373 _candidat.short_description = "Candidat"
374 _candidat.admin_order_field = "nom"
375 _candidat.allow_tags = True
376
377 def _date_creation(self, obj):
378 return obj.date_creation
379 _date_creation.admin_order_field = "date_creation"
380 _date_creation.short_description = "Date de réception"
381
382 ### Actions à afficher
383 def get_actions(self, request):
384 actions = super(CandidatAdmin, self).get_actions(request)
385 del actions['delete_selected']
386 return actions
387
388 ### Envoyer un courriel à des candidats
389 def envoyer_courriel_candidats(modeladmin, obj, candidats):
390 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
391
392 return HttpResponseRedirect(
393 reverse('selectionner_template') + "?ids=%s" % (",".join(selected))
394 )
395 envoyer_courriel_candidats.short_description = u'Envoyer courriel'
396
397 ### Changer le statut à des candidats
398 def changer_statut(modeladmin, request, queryset):
399 if request.POST.get('post'):
400 queryset.update(statut=request.POST.get('statut'))
401 return None
402
403 context = {
404 'action_checkbox_name': admin.helpers.ACTION_CHECKBOX_NAME,
405 'queryset': queryset,
406 'status': STATUT_CHOICES,
407 }
408
409 return render_to_response("recrutement/selectionner_statut.html",
410 context, context_instance = RequestContext(request))
411
412 changer_statut.short_description = u'Changer statut'
413
414 ### Évaluer un candidat
415 def evaluer_candidat(self, obj):
416 return "<a href='%s?candidat__id__exact=%s'>" \
417 "Évaluer le candidat</a>" % (
418 reverse('admin:recrutement_candidatevaluation_changelist'),
419 obj.id
420 )
421 evaluer_candidat.allow_tags = True
422 evaluer_candidat.short_description = 'Évaluation'
423
424 ### Afficher un candidat
425 def afficher_candidat(self, obj):
426 items = [u"<li><a href='%s%s'>%s</li>" % \
427 (settings.OE_PRIVE_MEDIA_URL, pj.path, pj.get_nom_display()) \
428 for pj in obj.pieces_jointes()]
429 html = "<a href='%s'>Candidature</a>" % (
430 reverse('admin:recrutement_proxycandidat_change', args=(obj.id,))
431 )
432 return "%s<ul>%s</ul>" % (html, "\n".join(items))
433 afficher_candidat.allow_tags = True
434 afficher_candidat.short_description = u'Détails du candidat'
435
436 ### Voir l'offre d'emploi
437 def voir_offre_emploi(self, obj):
438 return "<a href='%s'>Voir l'offre d'emploi</a>" % (reverse(
439 'admin:recrutement_proxyoffreemploi_change',
440 args=(obj.offre_emploi.id,)
441 ))
442 voir_offre_emploi.allow_tags = True
443 voir_offre_emploi.short_description = "Afficher l'offre d'emploi"
444
445 ### Calculer la moyenne des notes
446 def calculer_moyenne(self, obj):
447 evaluations = CandidatEvaluation.objects.filter(candidat=obj)
448
449 notes = [evaluation.note for evaluation in evaluations \
450 if evaluation.note is not None]
451
452 if len(notes) > 0:
453 moyenne_votes = round(float(sum(notes)) / len(notes), 2)
454 else:
455 moyenne_votes = "Non disponible"
456
457 totales = len(evaluations)
458 faites = len(notes)
459
460 if obj.statut == 'REC':
461 if totales == faites:
462 color = "green"
463 elif faites > 0 and float(totales) / float(faites) >= 2:
464 color = "orange"
465 else:
466 color = "red"
467 else:
468 color = "black"
469
470 return """<span style="color: %s;">%s (%s/%s)</span>""" % (
471 color, moyenne_votes, faites, totales
472 )
473 calculer_moyenne.allow_tags = True
474 calculer_moyenne.short_description = "Moyenne"
475 calculer_moyenne.admin_order_field = ""
476
477 ### Permissions add, delete, change
478 def has_add_permission(self, request):
479 user_groupes = request.user.groups.all()
480 if request.user.is_superuser is True or \
481 grp_correspondants_rh in user_groupes or \
482 grp_drh in user_groupes or \
483 grp_drh2 in user_groupes or \
484 grp_directeurs_bureau in user_groupes or \
485 grp_administrateurs in user_groupes or \
486 grp_haute_direction in user_groupes:
487 return True
488 return False
489
490 def has_delete_permission(self, request, obj=None):
491 user_groupes = request.user.groups.all()
492 if request.user.is_superuser is True or \
493 grp_correspondants_rh in user_groupes or \
494 grp_drh in user_groupes or \
495 grp_drh2 in user_groupes or \
496 grp_directeurs_bureau in user_groupes or \
497 grp_administrateurs in user_groupes or \
498 grp_haute_direction in user_groupes:
499 return True
500 return False
501
502 def has_change_permission(self, request, obj=None):
503 user_groupes = request.user.groups.all()
504 if request.user.is_superuser is True or \
505 grp_correspondants_rh in user_groupes or \
506 grp_drh in user_groupes or \
507 grp_drh2 in user_groupes or \
508 grp_directeurs_bureau in user_groupes or \
509 grp_administrateurs in user_groupes or \
510 grp_haute_direction in user_groupes:
511 return True
512 return False
513
514 def formfield_for_foreignkey(self, db_field, request, **kwargs):
515 if db_field.name == 'offre_emploi':
516 employe = get_emp(request.user)
517 kwargs["queryset"] = OffreEmploi.objects.filter(region=employe.implantation.region)
518 return db_field.formfield(**kwargs)
519 return super(CandidatAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
520
521 def get_changelist(self, request, **kwargs):
522 return OrderedChangeList
523
524 def queryset(self, request):
525 """
526 Spécifie un queryset limité, autrement Django exécute un
527 select_related() sans paramètre, ce qui a pour effet de charger tous
528 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
529 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
530 génération d'une requête infinie.
531 """
532 qs = self.model._default_manager.get_query_set() \
533 .select_related('offre_emploi') \
534 .annotate(moyenne=Avg('evaluations__note'))
535
536 user_groupes = request.user.groups.all()
537 if grp_drh in user_groupes or \
538 grp_drh2 in user_groupes or \
539 grp_haute_direction in user_groupes:
540 return qs
541
542 if grp_directeurs_bureau in user_groupes or \
543 grp_correspondants_rh in user_groupes or \
544 grp_administrateurs in user_groupes:
545 employe = get_emp(request.user)
546 return qs.filter(offre_emploi__region=employe.implantation.region)
547
548 if Evaluateur.objects.filter(user=request.user).exists():
549 evaluateur = Evaluateur.objects.get(user=request.user)
550 candidat_ids = [e.candidat.id for e in
551 CandidatEvaluation.objects.filter(evaluateur=evaluateur)]
552 return qs.filter(id__in=candidat_ids)
553 return qs.none()
554
555
556 class ProxyCandidatAdmin(CandidatAdmin):
557 list_editable = ()
558 readonly_fields = (
559 'statut', 'offre_emploi', 'prenom', 'nom', 'genre', 'nationalite',
560 'situation_famille', 'nombre_dependant', 'telephone', 'email',
561 'adresse', 'ville', 'etat_province', 'code_postal', 'pays',
562 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
563 'domaine_professionnel', 'pieces_jointes'
564 )
565 fieldsets = (
566 ("Offre d'emploi", {
567 'fields': ('offre_emploi', )
568 }),
569 ('Informations personnelles', {
570 'fields': (
571 'prenom', 'nom', 'genre', 'nationalite', 'situation_famille',
572 'nombre_dependant'
573 )
574 }),
575 ('Coordonnées', {
576 'fields': (
577 'telephone', 'email', 'adresse', 'ville', 'etat_province',
578 'code_postal', 'pays'
579 )
580 }),
581 ('Informations professionnelles', {
582 'fields': (
583 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
584 'domaine_professionnel'
585 )
586 }),
587 )
588 inlines = (CandidatEvaluationInline, )
589
590 def has_add_permission(self, request):
591 return False
592
593 def has_delete_permission(self, request, obj=None):
594 return False
595
596 def has_change_permission(self, request, obj=None):
597 if obj is not None:
598 evaluateur = Evaluateur.objects.get(user=request.user)
599 for e in obj.evaluations.all():
600 if e.evaluateur == evaluateur:
601 return True
602
603 return not super(ProxyCandidatAdmin, self).has_change_permission(request, obj)
604
605 def get_actions(self, request):
606 return None
607
608
609 class CandidatPieceAdmin(admin.ModelAdmin):
610 list_display = ('nom', 'candidat', )
611
612 ### Queryset
613 def queryset(self, request):
614 """
615 Spécifie un queryset limité, autrement Django exécute un
616 select_related() sans paramètre, ce qui a pour effet de charger tous
617 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
618 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
619 génération d'une requête infinie. Affiche la liste de candidats que
620 si le user connecté possède un Evaluateur
621 """
622 qs = self.model._default_manager.get_query_set()
623 return qs.select_related('candidat')
624
625
626 class EvaluateurAdmin(BaseAdmin, VersionAdmin):
627 fieldsets = (
628 ("Utilisateur", {
629 'fields': ('user',)
630 }),
631 )
632
633 ### Actions à afficher
634 def get_actions(self, request):
635 actions = super(EvaluateurAdmin, self).get_actions(request)
636 del actions['delete_selected']
637 return actions
638
639 ### Permissions add, delete, change
640 def has_add_permission(self, request):
641 user_groupes = request.user.groups.all()
642 if request.user.is_superuser is True or \
643 grp_drh in user_groupes or \
644 grp_drh2 in user_groupes or \
645 grp_haute_direction in user_groupes:
646 return True
647 return False
648
649 def has_delete_permission(self, request, obj=None):
650 user_groupes = request.user.groups.all()
651 if request.user.is_superuser is True or \
652 grp_drh in user_groupes or \
653 grp_drh2 in user_groupes or \
654 grp_haute_direction in user_groupes:
655 return True
656 return False
657
658 def has_change_permission(self, request, obj=None):
659 user_groupes = request.user.groups.all()
660 if request.user.is_superuser is True or \
661 grp_drh in user_groupes or \
662 grp_drh2 in user_groupes or \
663 grp_haute_direction in user_groupes:
664 return True
665 return False
666
667
668 class CandidatEvaluationAdmin(BaseAdmin):
669 search_fields = ('candidat__nom', 'candidat__prenom')
670 list_display = (
671 '_candidat', '_statut', '_offre_emploi', 'evaluateur', '_note',
672 '_commentaire'
673 )
674 readonly_fields = ('candidat', 'evaluateur')
675 list_filter = ('candidat__statut', 'candidat__offre_emploi',)
676 fieldsets = (
677 ('Évaluation du candidat', {
678 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
679 }),
680 )
681
682 def get_actions(self, request):
683 # on stocke l'evaluateur connecté (pas forcément la meilleure place...)
684 try:
685 self.evaluateur = Evaluateur.objects.get(user=request.user)
686 except:
687 self.evaluateur = None
688
689 actions = super(CandidatEvaluationAdmin, self).get_actions(request)
690 del actions['delete_selected']
691 return actions
692
693 ### Afficher la note
694 def _note(self, obj):
695 """
696 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
697 un lien pour Évaluer le candidat.
698 Sinon afficher la note.
699 """
700 page = self.model.__name__.lower()
701 redirect_url = 'admin:recrutement_%s_change' % page
702
703 if obj.note is None:
704 label = "Candidat non évalué"
705 else:
706 label = obj.note
707
708 if self.evaluateur == obj.evaluateur:
709 return "<a href='%s'>%s</a>" % (
710 reverse(redirect_url, args=(obj.id,)), label
711 )
712 else:
713 return label
714 _note.allow_tags = True
715 _note.short_description = "Note"
716 _note.admin_order_field = 'note'
717
718 def _statut(self, obj):
719 return obj.candidat.get_statut_display()
720 _statut.order_field = 'candidat__statut'
721 _statut.short_description = 'Statut'
722
723 ### Lien en lecture seule vers le candidat
724 def _candidat(self, obj):
725 return "<a href='%s'>%s</a>" \
726 % (reverse('admin:recrutement_proxycandidat_change',
727 args=(obj.candidat.id,)), obj.candidat)
728 _candidat.allow_tags = True
729 _candidat.short_description = 'Candidat'
730
731 ### Afficher commentaire
732 def _commentaire(self, obj):
733 """
734 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
735 dans le champ commentaire, Aucun au lieu de (None)
736 Sinon afficher la note.
737 """
738 if obj.commentaire is None:
739 return "Aucun"
740 return obj.commentaire
741 _commentaire.allow_tags = True
742 _commentaire.short_description = "Commentaire"
743
744 ### Afficher offre d'emploi
745 def _offre_emploi(self, obj):
746 return "<a href='%s'>%s</a>" % \
747 (reverse('admin:recrutement_proxyoffreemploi_change',
748 args=(obj.candidat.offre_emploi.id,)), obj.candidat.offre_emploi)
749 _offre_emploi.allow_tags = True
750 _offre_emploi.short_description = "Voir offre d'emploi"
751
752 def has_add_permission(self, request):
753 return False
754
755 def has_delete_permission(self, request, obj=None):
756 return False
757
758 def has_change_permission(self, request, obj=None):
759 """
760 Permettre la visualisation dans la changelist
761 mais interdire l'accès à modifier l'objet si l'évaluateur n'est pas
762 le request.user
763 """
764 user_groupes = request.user.groups.all()
765
766 if request.user.is_superuser or \
767 grp_drh in user_groupes or \
768 grp_drh2 in user_groupes or \
769 grp_correspondants_rh in user_groupes or \
770 grp_directeurs_bureau in user_groupes or \
771 grp_administrateurs in user_groupes or \
772 grp_haute_direction in user_groupes:
773 is_recrutement = True
774 else:
775 is_recrutement = False
776
777 return is_recrutement
778
779 def queryset(self, request):
780 """
781 Afficher uniquement les évaluations de l'évaluateur, sauf si
782 l'utilisateur est dans les groupes suivants.
783 """
784 qs = self.model._default_manager.get_query_set() \
785 .select_related('offre_emploi')
786 user_groupes = request.user.groups.all()
787
788 if grp_drh in user_groupes or \
789 grp_drh2 in user_groupes or \
790 grp_correspondants_rh in user_groupes or \
791 grp_directeurs_bureau in user_groupes or \
792 grp_administrateurs in user_groupes or \
793 grp_haute_direction in user_groupes:
794 return qs.filter(candidat__statut__in=('REC', 'SEL'))
795
796 evaluateur = Evaluateur.objects.get(user=request.user)
797 candidats_evaluations = \
798 CandidatEvaluation.objects.filter(evaluateur=evaluateur,
799 candidat__statut__in=('REC', ))
800 candidats_evaluations_ids = [ce.id for ce in candidats_evaluations]
801 return qs.filter(id__in=candidats_evaluations_ids)
802
803
804 class MesCandidatEvaluationAdmin(CandidatEvaluationAdmin):
805 list_filter = []
806
807 def has_change_permission(self, request, obj=None):
808 try:
809 Evaluateur.objects.get(user=request.user)
810 is_evaluateur = True
811 except:
812 is_evaluateur = False
813
814 if obj is None and is_evaluateur:
815 return True
816
817 try:
818 return request.user == obj.evaluateur.user
819 except:
820 return False
821
822 def queryset(self, request):
823 qs = self.model._default_manager.get_query_set() \
824 .select_related('offre_emploi')
825 evaluateur = Evaluateur.objects.get(user=request.user)
826 candidats_evaluations = \
827 CandidatEvaluation.objects.filter(evaluateur=evaluateur,
828 candidat__statut__in=('REC', ))
829 candidats_evaluations_ids = [ce.id for ce in candidats_evaluations]
830 return qs.filter(id__in=candidats_evaluations_ids)
831
832
833 class CourrielTemplateAdmin(BaseAdmin, VersionAdmin):
834 ### Actions à afficher
835 def get_actions(self, request):
836 actions = super(CourrielTemplateAdmin, self).get_actions(request)
837 del actions['delete_selected']
838 return actions
839
840 admin.site.register(OffreEmploi, OffreEmploiAdmin)
841 admin.site.register(ProxyOffreEmploi, ProxyOffreEmploiAdmin)
842 admin.site.register(Candidat, CandidatAdmin)
843 admin.site.register(ProxyCandidat, ProxyCandidatAdmin)
844 admin.site.register(CandidatEvaluation, CandidatEvaluationAdmin)
845 admin.site.register(MesCandidatEvaluation, MesCandidatEvaluationAdmin)
846 admin.site.register(Evaluateur, EvaluateurAdmin)
847 admin.site.register(CourrielTemplate, CourrielTemplateAdmin)