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