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