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