Merge remote-tracking branch 'origin/recrutement' into recrutement
[auf_rh_dae.git] / project / recrutement / admin.py
index aa602a4..a775ac3 100644 (file)
@@ -1,33 +1,49 @@
 # -*- encoding: utf-8 -*-
 
 import textwrap
-from django.core.urlresolvers import reverse
-from django.http import HttpResponseRedirect
+
+from django.conf import settings
 from django.contrib import admin
-from django.forms.models import BaseInlineFormSet
+from django.core.urlresolvers import reverse
 from django.db.models import Avg
-from django.conf import settings
 from django.shortcuts import render_to_response
 from django.template import RequestContext
 
-from reversion.admin import VersionAdmin
-from datamaster_modeles.models import Region, Bureau
-from project.rh import models as rh
 from auf.django.emploi.models import STATUT_CHOICES
+from django.forms.models import BaseInlineFormSet
+from django.http import HttpResponseRedirect
+from django.shortcuts import redirect
+from reversion.admin import VersionAdmin
+
+from auf.django.emploi.models import OffreEmploi, Candidat, CandidatPiece
+from auf.django.references.models import Region, Bureau
 
-from project.dae.utils import get_employe_from_user as get_emp
-from recrutement.models import *
-from recrutement.workflow import grp_drh_recrutement, grp_drh2_recrutement, \
-        grp_directeurs_bureau_recrutement, \
-        grp_administrateurs_recrutement, \
-        grp_correspondants_rh_recrutement, \
-        grp_haute_direction_recrutement
+from project.groups import get_employe_from_user as get_emp
+from project.rh import models as rh
 
-from recrutement.forms import *
+from project.recrutement.forms import OffreEmploiForm
+from project.recrutement.groups import \
+        grp_drh, grp_drh2, \
+        grp_directeurs_bureau, \
+        grp_administrateurs, \
+        grp_correspondants_rh, \
+        grp_haute_direction
+from project.recrutement.models import \
+        Evaluateur, CandidatEvaluation, \
+        ProxyOffreEmploi, ProxyCandidat, MesCandidatEvaluation, \
+        CourrielTemplate
+        
 
 ### CONSTANTES
 IMPLANTATIONS_CENTRALES = [15, 19]
 
+
+class BaseAdmin(admin.ModelAdmin):
+
+    class Media:
+        css = {'screen': ('css/admin_custom.css',)}
+
+
 class OrderedChangeList(admin.views.main.ChangeList):
     """
     Surcharge pour appliquer le order_by d'un annotate
@@ -37,10 +53,13 @@ class OrderedChangeList(admin.views.main.ChangeList):
         qs = qs.order_by('-moyenne')
         return qs
 
-class OffreEmploiAdmin(VersionAdmin):
+
+class OffreEmploiAdmin(BaseAdmin, VersionAdmin):
     date_hierarchy = 'date_creation'
-    list_display = ('nom', 'date_limite', 'region',  'statut', 
-                    'est_affiche', '_candidatsList', )
+    list_display = (
+        'nom', 'date_limite', 'region',  'statut', 'est_affiche',
+        '_candidatsList'
+    )
     exclude = ('actif', 'poste_nom', 'resume',)
     list_filter = ('statut',)
     actions = ['affecter_evaluateurs_offre_emploi', ]
@@ -53,18 +72,22 @@ class OffreEmploiAdmin(VersionAdmin):
         return actions
 
     ### Affecter un évaluateurs à des offres d'emploi
-    def affecter_evaluateurs_offre_emploi(modeladmin, obj, candidats):   
+    def affecter_evaluateurs_offre_emploi(modeladmin, obj, candidats):
         selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
 
-        return HttpResponseRedirect(reverse('affecter_evaluateurs_offre_emploi')+
-                "?ids=%s" % (",".join(selected)))
-    affecter_evaluateurs_offre_emploi.short_description = u'Affecter évaluateur(s)'
+        return HttpResponseRedirect(
+            reverse('affecter_evaluateurs_offre_emploi') +
+            "?ids=%s" % (",".join(selected))
+        )
+
+    affecter_evaluateurs_offre_emploi.short_description = \
+            u'Affecter évaluateur(s)'
 
     ### Afficher la liste des candidats pour l'offre d'emploi
-    def _candidatsList(self, obj):     
+    def _candidatsList(self, obj):
         return "<a href='%s?offre_emploi__id__exact=%s'>Voir les candidats \
             </a>" % (reverse('admin:recrutement_candidat_changelist'), obj.id)
-    _candidatsList.allow_tags = True 
+    _candidatsList.allow_tags = True
     _candidatsList.short_description = "Afficher la liste des candidats"
 
     ### Formulaire
@@ -72,72 +95,77 @@ class OffreEmploiAdmin(VersionAdmin):
         form = super(OffreEmploiAdmin, self).get_form(request, obj, **kwargs)
         employe = get_emp(request.user)
         user_groupes = request.user.groups.all()
-        
-    
+
         # Region
-        if form.declared_fields.has_key('region'):
+        if 'region' in form.declared_fields:
             region_field = form.declared_fields['region']
         else:
             region_field = form.base_fields['region']
 
-        if grp_drh_recrutement in user_groupes or \
-            grp_drh2_recrutement in user_groupes or \
-            grp_haute_direction_recrutement in user_groupes:
+        if grp_drh in user_groupes or \
+            grp_drh2 in user_groupes or \
+            grp_haute_direction in user_groupes:
             region_field.queryset = Region.objects.all()
         else:
             region_field.queryset = Region.objects.\
                                     filter(id=employe.implantation.region.id)
-        
+
         # Poste
-        if form.declared_fields.has_key('poste'):
+        if 'poste' in form.declared_fields:
             poste_field = form.declared_fields['poste']
         else:
             poste_field = form.base_fields['poste']
 
-        if grp_drh_recrutement in user_groupes or \
-            grp_drh2_recrutement in user_groupes or \
-            grp_haute_direction_recrutement in user_groupes:
+        if grp_drh in user_groupes or \
+            grp_drh2 in user_groupes or \
+            grp_haute_direction in user_groupes:
             poste_field.queryset = rh.Poste.objects.all()
         else:
             poste_field.queryset = rh.Poste.objects.\
                     filter(implantation__region=employe.implantation.region).\
                     exclude(implantation__in=IMPLANTATIONS_CENTRALES)
-        
+
         # Bureau
-        if form.declared_fields.has_key('bureau'):
+        if 'bureau' in form.declared_fields:
             bureau_field = form.declared_fields['bureau']
         else:
             bureau_field = form.base_fields['bureau']
 
-        if grp_drh_recrutement in user_groupes or \
-            grp_drh2_recrutement in user_groupes or \
-            grp_haute_direction_recrutement in user_groupes:
+        if grp_drh in user_groupes or \
+            grp_drh2 in user_groupes or \
+            grp_haute_direction in user_groupes:
             bureau_field.queryset = Bureau.objects.all()
         else:
-            bureau_field.queryset = Bureau.objects.\
-                                    filter(region=employe.implantation.region)   
-         
+            bureau_field.queryset = \
+                    Bureau.objects.filter(region=employe.implantation.region)
+
         return form
-        
+
     ### Queryset
+
     def queryset(self, request):
-        qs = self.model._default_manager.get_query_set().select_related('offre_emploi')
+        qs = self.model._default_manager.get_query_set() \
+                .select_related('offre_emploi')
         user_groupes = request.user.groups.all()
-        if grp_drh_recrutement in user_groupes or \
-            grp_drh2_recrutement in user_groupes or \
-            grp_haute_direction_recrutement in user_groupes:
+        if grp_drh in user_groupes or \
+            grp_drh2 in user_groupes or \
+            grp_haute_direction in user_groupes:
             return qs
 
-        if grp_directeurs_bureau_recrutement in user_groupes or \
-            grp_correspondants_rh_recrutement in user_groupes or \
-            grp_administrateurs_recrutement in user_groupes:
+        if grp_directeurs_bureau in user_groupes or \
+            grp_correspondants_rh in user_groupes or \
+            grp_administrateurs in user_groupes:
             employe = get_emp(request.user)
             return qs.filter(region=employe.implantation.region)
 
         if  Evaluateur.objects.filter(user=request.user).exists():
             evaluateur = Evaluateur.objects.get(user=request.user)
-            offre_ids = [e.candidat.offre_emploi_id for e in
-                    CandidatEvaluation.objects.select_related('candidat').filter(evaluateur=evaluateur)]
+            offre_ids = [
+                e.candidat.offre_emploi_id
+                for e in CandidatEvaluation.objects
+                .select_related('candidat')
+                .filter(evaluateur=evaluateur)
+            ]
             return qs.filter(id__in=offre_ids)
 
         return qs.none()
@@ -146,64 +174,67 @@ class OffreEmploiAdmin(VersionAdmin):
     def has_add_permission(self, request):
         user_groupes = request.user.groups.all()
         if request.user.is_superuser is True or \
-            grp_drh_recrutement in user_groupes or \
-            grp_drh2_recrutement in user_groupes or \
-            grp_directeurs_bureau_recrutement in user_groupes or \
-            grp_administrateurs_recrutement in user_groupes or \
-            grp_haute_direction_recrutement in user_groupes:
+            grp_drh in user_groupes or \
+            grp_drh2 in user_groupes or \
+            grp_directeurs_bureau in user_groupes or \
+            grp_administrateurs in user_groupes or \
+            grp_haute_direction in user_groupes:
             return True
-        return False  
+        return False
 
     def has_delete_permission(self, request, obj=None):
         user_groupes = request.user.groups.all()
         if request.user.is_superuser is True or \
-            grp_drh_recrutement in user_groupes or \
-            grp_drh2_recrutement in user_groupes or \
-            grp_directeurs_bureau_recrutement in user_groupes or \
-            grp_administrateurs_recrutement in user_groupes or \
-            grp_haute_direction_recrutement in user_groupes:
+            grp_drh in user_groupes or \
+            grp_drh2 in user_groupes or \
+            grp_directeurs_bureau in user_groupes or \
+            grp_administrateurs in user_groupes or \
+            grp_haute_direction in user_groupes:
             return True
-        return False   
+        return False
 
     def has_change_permission(self, request, obj=None):
         user_groupes = request.user.groups.all()
         if request.user.is_superuser is True or \
-            grp_drh_recrutement in user_groupes or \
-            grp_drh2_recrutement in user_groupes or \
-            grp_directeurs_bureau_recrutement in user_groupes or \
-            grp_administrateurs_recrutement in user_groupes or \
-            grp_haute_direction_recrutement in user_groupes:
+            grp_drh in user_groupes or \
+            grp_drh2 in user_groupes or \
+            grp_directeurs_bureau in user_groupes or \
+            grp_administrateurs in user_groupes or \
+            grp_haute_direction in user_groupes:
             return True
-        return False   
+        return False
+
 
 class ProxyOffreEmploiAdmin(OffreEmploiAdmin):
-    list_display = ('nom', 'date_limite', 'region', 'statut', 
-                    'est_affiche')
-    readonly_fields = ('description', 'bureau', 'duree_affectation', 
-                        'renumeration', 'debut_affectation', 'lieu_affectation',
-                        'nom', 'resume', 'date_limite', 'region', 'poste')
+    list_display = (
+        'nom', 'date_limite', 'region', 'statut', 'est_affiche'
+    )
+    readonly_fields = (
+        'description', 'bureau', 'duree_affectation', 'renumeration',
+        'debut_affectation', 'lieu_affectation', 'nom', 'resume',
+        'date_limite', 'region', 'poste'
+    )
     fieldsets = (
         ('Nom', {
-            'fields': ('nom', )        
+            'fields': ('nom',)
         }),
         ('Description générale', {
-            'fields': ('description', 'date_limite', )        
+            'fields': ('description', 'date_limite',)
         }),
         ('Coordonnées', {
             'fields': ('lieu_affectation', 'bureau', 'region', 'poste',)
         }),
         ('Autre', {
-            'fields': ('debut_affectation', 'duree_affectation',
-                        'renumeration', )
+            'fields': (
+                'debut_affectation', 'duree_affectation', 'renumeration',
+            )
         }),
-    )        
+    )
     inlines = []
 
-
-    ### Lieu de redirection après le change 
+    ### Lieu de redirection après le change
     def response_change(self, request, obj):
-        return HttpResponseRedirect(reverse\
-                            ('admin:recrutement_proxyoffreemploi_changelist'))
+        return redirect('admin:recrutement_proxyoffreemploi_changelist')
 
     ### Formulaire
     def get_form(self, request, obj=None, **kwargs):
@@ -220,12 +251,12 @@ class ProxyOffreEmploiAdmin(OffreEmploiAdmin):
     def has_change_permission(self, request, obj=None):
         user_groupes = request.user.groups.all()
         if request.user.is_superuser is True or \
-            grp_correspondants_rh_recrutement in user_groupes or \
-            grp_drh_recrutement in user_groupes or \
-            grp_drh2_recrutement in user_groupes or \
-            grp_directeurs_bureau_recrutement in user_groupes or \
-            grp_administrateurs_recrutement in user_groupes or \
-            grp_haute_direction_recrutement in user_groupes:
+            grp_correspondants_rh in user_groupes or \
+            grp_drh in user_groupes or \
+            grp_drh2 in user_groupes or \
+            grp_directeurs_bureau in user_groupes or \
+            grp_administrateurs in user_groupes or \
+            grp_haute_direction in user_groupes:
             return True
 
         if obj is not None:
@@ -233,12 +264,14 @@ class ProxyOffreEmploiAdmin(OffreEmploiAdmin):
 
         return False
 
+
 class CandidatPieceInline(admin.TabularInline):
     model = CandidatPiece
     fields = ('candidat', 'nom', 'path',)
     extra = 1
     max_num = 3
 
+
 class ReadOnlyCandidatPieceInline(CandidatPieceInline):
     readonly_fields = ('candidat', 'nom', 'path', )
     cand_delete = False
@@ -250,7 +283,8 @@ class CandidatEvaluationInlineFormSet(BaseInlineFormSet):
     """
     def __init__(self, *args, **kwargs):
         super(CandidatEvaluationInlineFormSet, self).__init__(*args, **kwargs)
-        self.can_delete = False 
+        self.can_delete = False
+
 
 class CandidatEvaluationInline(admin.TabularInline):
     model = CandidatEvaluation
@@ -259,17 +293,18 @@ class CandidatEvaluationInline(admin.TabularInline):
     extra = 0
     formset = CandidatEvaluationInlineFormSet
 
-    ### Fields readonly    
+    ### Fields readonly
     def get_readonly_fields(self, request, obj=None):
         """
         Empêche la modification des évaluations
         """
         if obj:
-            return self.readonly_fields+('evaluateur', 'note', 'commentaire')
+            return self.readonly_fields + ('evaluateur', 'note', 'commentaire')
         return self.readonly_fields
 
-class CandidatAdmin(VersionAdmin):
-    search_fields = ('nom', 'prenom' )
+
+class CandidatAdmin(BaseAdmin, VersionAdmin):
+    search_fields = ('nom', 'prenom')
     exclude = ('actif', )
     list_editable = ('statut', )
     list_display = ('_candidat', 'offre_emploi',
@@ -282,17 +317,23 @@ class CandidatAdmin(VersionAdmin):
             'fields': ('offre_emploi', )
         }),
         ('Informations personnelles', {
-            'fields': ('prenom','nom','genre', 'nationalite',
-                        'situation_famille', 'nombre_dependant',)        
+            'fields': (
+                'prenom', 'nom', 'genre', 'nationalite',
+                'situation_famille', 'nombre_dependant'
+            )
         }),
         ('Coordonnées', {
-            'fields': ('telephone', 'email', 'adresse', 'ville', 
-                        'etat_province', 'code_postal', 'pays', )
+            'fields': (
+                'telephone', 'email', 'adresse', 'ville', 'etat_province',
+                'code_postal', 'pays'
+            )
         }),
         ('Informations professionnelles', {
-            'fields': ('niveau_diplome','employeur_actuel', 
-                        'poste_actuel', 'domaine_professionnel',)
-        }),  
+            'fields': (
+                'niveau_diplome', 'employeur_actuel', 'poste_actuel',
+                'domaine_professionnel'
+            )
+        }),
         ('Traitement', {
             'fields': ('statut', )
         }),
@@ -304,8 +345,7 @@ class CandidatAdmin(VersionAdmin):
     actions = ['envoyer_courriel_candidats', 'changer_statut']
 
     def _candidat(self, obj):
-        txt = u"%s %s (%s)" % ( obj.nom.upper(), obj.prenom,
-                obj.genre)
+        txt = u"%s %s (%s)" % (obj.nom.upper(), obj.prenom, obj.genre)
         txt = textwrap.wrap(txt, 30)
         return "<br/>".join(txt)
     _candidat.short_description = "Candidat"
@@ -324,11 +364,12 @@ class CandidatAdmin(VersionAdmin):
         return actions
 
     ### Envoyer un courriel à des candidats
-    def envoyer_courriel_candidats(modeladmin, obj, candidats):   
+    def envoyer_courriel_candidats(modeladmin, obj, candidats):
         selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
 
-        return HttpResponseRedirect(reverse('selectionner_template')+
-                "?ids=%s" % (",".join(selected)))
+        return HttpResponseRedirect(
+            reverse('selectionner_template') + "?ids=%s" % (",".join(selected))
+        )
     envoyer_courriel_candidats.short_description = u'Envoyer courriel'
 
     ### Changer le statut à des candidats
@@ -350,10 +391,12 @@ class CandidatAdmin(VersionAdmin):
 
     ### Évaluer un candidat
     def evaluer_candidat(self, obj):
-        return "<a href='%s?candidat__id__exact=%s'>Évaluer le candidat</a>" % \
-            (reverse('admin:recrutement_candidatevaluation_changelist'), 
-            obj.id)
-    evaluer_candidat.allow_tags = True    
+        return "<a href='%s?candidat__id__exact=%s'>" \
+                "Évaluer le candidat</a>" % (
+                    reverse('admin:recrutement_candidatevaluation_changelist'),
+                    obj.id
+                )
+    evaluer_candidat.allow_tags = True
     evaluer_candidat.short_description = 'Évaluation'
 
     ### Afficher un candidat
@@ -361,17 +404,19 @@ class CandidatAdmin(VersionAdmin):
         items = [u"<li><a href='%s%s'>%s</li>" % \
                 (settings.OE_PRIVE_MEDIA_URL, pj.path, pj.get_nom_display()) \
                 for pj in obj.pieces_jointes()]
-        html =  "<a href='%s'>Voir le candidat</a>" % \
-            (reverse('admin:recrutement_proxycandidat_change', args=(obj.id,)))
+        html = "<a href='%s'>Voir le candidat</a>" % (
+            reverse('admin:recrutement_proxycandidat_change', args=(obj.id,))
+        )
         return "%s<ul>%s</ul>" % (html, "\n".join(items))
-    afficher_candidat.allow_tags = True    
+    afficher_candidat.allow_tags = True
     afficher_candidat.short_description = u'Détails du candidat'
 
     ### Voir l'offre d'emploi
     def voir_offre_emploi(self, obj):
-        return "<a href='%s'>Voir l'offre d'emploi</a>" % \
-        (reverse('admin:recrutement_proxyoffreemploi_change', 
-                    args=(obj.offre_emploi.id,)))
+        return "<a href='%s'>Voir l'offre d'emploi</a>" % (reverse(
+            'admin:recrutement_proxyoffreemploi_change',
+            args=(obj.offre_emploi.id,)
+        ))
     voir_offre_emploi.allow_tags = True
     voir_offre_emploi.short_description = "Afficher l'offre d'emploi"
 
@@ -381,7 +426,7 @@ class CandidatAdmin(VersionAdmin):
 
         notes = [evaluation.note for evaluation in evaluations \
                     if evaluation.note is not None]
+
         if len(notes) > 0:
             moyenne_votes = round(float(sum(notes)) / len(notes), 2)
         else:
@@ -390,7 +435,7 @@ class CandidatAdmin(VersionAdmin):
         totales = len(evaluations)
         faites = len(notes)
 
-        if obj.statut  == 'REC':
+        if obj.statut == 'REC':
             if totales == faites:
                 color = "green"
             elif faites > 0 and float(totales) / float(faites) >= 2:
@@ -400,7 +445,9 @@ class CandidatAdmin(VersionAdmin):
         else:
             color = "black"
 
-        return """<span style="color: %s;">%s (%s/%s)</span>""" % (color, moyenne_votes, faites, totales)
+        return """<span style="color: %s;">%s (%s/%s)</span>""" % (
+            color, moyenne_votes, faites, totales
+        )
     calculer_moyenne.allow_tags = True
     calculer_moyenne.short_description = "Moyenne"
     calculer_moyenne.admin_order_field = ""
@@ -409,36 +456,36 @@ class CandidatAdmin(VersionAdmin):
     def has_add_permission(self, request):
         user_groupes = request.user.groups.all()
         if request.user.is_superuser is True or \
-            grp_correspondants_rh_recrutement in user_groupes or \
-            grp_drh_recrutement in user_groupes or \
-            grp_drh2_recrutement in user_groupes or \
-            grp_directeurs_bureau_recrutement in user_groupes or \
-            grp_administrateurs_recrutement in user_groupes or \
-            grp_haute_direction_recrutement in user_groupes:
+            grp_correspondants_rh in user_groupes or \
+            grp_drh in user_groupes or \
+            grp_drh2 in user_groupes or \
+            grp_directeurs_bureau in user_groupes or \
+            grp_administrateurs in user_groupes or \
+            grp_haute_direction in user_groupes:
             return True
-        return False   
+        return False
 
     def has_delete_permission(self, request, obj=None):
         user_groupes = request.user.groups.all()
         if request.user.is_superuser is True or \
-            grp_correspondants_rh_recrutement in user_groupes or \
-            grp_drh_recrutement in user_groupes or \
-            grp_drh2_recrutement in user_groupes or \
-            grp_directeurs_bureau_recrutement in user_groupes or \
-            grp_administrateurs_recrutement in user_groupes or \
-            grp_haute_direction_recrutement in user_groupes:
+            grp_correspondants_rh in user_groupes or \
+            grp_drh in user_groupes or \
+            grp_drh2 in user_groupes or \
+            grp_directeurs_bureau in user_groupes or \
+            grp_administrateurs in user_groupes or \
+            grp_haute_direction in user_groupes:
             return True
-        return False   
+        return False
 
     def has_change_permission(self, request, obj=None):
         user_groupes = request.user.groups.all()
         if request.user.is_superuser is True or \
-            grp_correspondants_rh_recrutement in user_groupes or \
-            grp_drh_recrutement in user_groupes or \
-            grp_drh2_recrutement in user_groupes or \
-            grp_directeurs_bureau_recrutement in user_groupes or \
-            grp_administrateurs_recrutement in user_groupes or \
-            grp_haute_direction_recrutement in user_groupes:
+            grp_correspondants_rh in user_groupes or \
+            grp_drh in user_groupes or \
+            grp_drh2 in user_groupes or \
+            grp_directeurs_bureau in user_groupes or \
+            grp_administrateurs in user_groupes or \
+            grp_haute_direction in user_groupes:
             return True
         return False
 
@@ -447,25 +494,25 @@ class CandidatAdmin(VersionAdmin):
 
     def queryset(self, request):
         """
-        Spécifie un queryset limité, autrement Django exécute un 
-        select_related() sans paramètre, ce qui a pour effet de charger tous 
-        les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les 
-        modèles de Region, il existe plusieurs boucles, ce qui conduit à la 
+        Spécifie un queryset limité, autrement Django exécute un
+        select_related() sans paramètre, ce qui a pour effet de charger tous
+        les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
+        modèles de Region, il existe plusieurs boucles, ce qui conduit à la
         génération d'une requête infinie.
-        
         """
-        
-        qs = self.model._default_manager.get_query_set().select_related('offre_emploi').annotate(moyenne=Avg('evaluations__note'))
+        qs = self.model._default_manager.get_query_set() \
+                .select_related('offre_emploi') \
+                .annotate(moyenne=Avg('evaluations__note'))
 
         user_groupes = request.user.groups.all()
-        if grp_drh_recrutement in user_groupes or \
-            grp_drh2_recrutement in user_groupes or \
-            grp_haute_direction_recrutement in user_groupes:
+        if grp_drh in user_groupes or \
+            grp_drh2 in user_groupes or \
+            grp_haute_direction in user_groupes:
             return qs
 
-        if grp_directeurs_bureau_recrutement in user_groupes or \
-            grp_correspondants_rh_recrutement in user_groupes or \
-            grp_administrateurs_recrutement in user_groupes:
+        if grp_directeurs_bureau in user_groupes or \
+            grp_correspondants_rh in user_groupes or \
+            grp_administrateurs in user_groupes:
             employe = get_emp(request.user)
             return qs.filter(offre_emploi__region=employe.implantation.region)
 
@@ -474,33 +521,40 @@ class CandidatAdmin(VersionAdmin):
             candidat_ids = [e.candidat.id for e in
                     CandidatEvaluation.objects.filter(evaluateur=evaluateur)]
             return qs.filter(id__in=candidat_ids)
-        return qs.none()    
+        return qs.none()
 
 
 class ProxyCandidatAdmin(CandidatAdmin):
     list_editable = ()
-    readonly_fields = ('statut', 'offre_emploi', 'prenom', 'nom',
-                        'genre', 'nationalite', 'situation_famille', 
-                        'nombre_dependant', 'telephone', 'email', 'adresse', 
-                        'ville', 'etat_province', 'code_postal', 'pays', 
-                        'niveau_diplome', 'employeur_actuel', 'poste_actuel',
-                        'domaine_professionnel', 'pieces_jointes',)
+    readonly_fields = (
+        'statut', 'offre_emploi', 'prenom', 'nom', 'genre', 'nationalite',
+        'situation_famille', 'nombre_dependant', 'telephone', 'email',
+        'adresse', 'ville', 'etat_province', 'code_postal', 'pays',
+        'niveau_diplome', 'employeur_actuel', 'poste_actuel',
+        'domaine_professionnel', 'pieces_jointes'
+    )
     fieldsets = (
         ("Offre d'emploi", {
             'fields': ('offre_emploi', )
         }),
         ('Informations personnelles', {
-            'fields': ('prenom','nom','genre', 'nationalite',
-                        'situation_famille', 'nombre_dependant',)        
+            'fields': (
+                'prenom', 'nom', 'genre', 'nationalite', 'situation_famille',
+                'nombre_dependant'
+            )
         }),
         ('Coordonnées', {
-            'fields': ('telephone', 'email', 'adresse', 'ville', 
-                        'etat_province', 'code_postal', 'pays', )
+            'fields': (
+                'telephone', 'email', 'adresse', 'ville', 'etat_province',
+                'code_postal', 'pays'
+            )
         }),
         ('Informations professionnelles', {
-            'fields': ('niveau_diplome','employeur_actuel', 
-                        'poste_actuel', 'domaine_professionnel',)
-        }),   
+            'fields': (
+                'niveau_diplome', 'employeur_actuel', 'poste_actuel',
+                'domaine_professionnel'
+            )
+        }),
     )
     inlines = (CandidatEvaluationInline, )
 
@@ -513,12 +567,12 @@ class ProxyCandidatAdmin(CandidatAdmin):
     def has_change_permission(self, request, obj=None):
         user_groupes = request.user.groups.all()
         if request.user.is_superuser is True or \
-            grp_correspondants_rh_recrutement in user_groupes or \
-            grp_drh_recrutement in user_groupes or \
-            grp_drh2_recrutement in user_groupes or \
-            grp_directeurs_bureau_recrutement in user_groupes or \
-            grp_administrateurs_recrutement in user_groupes or \
-            grp_haute_direction_recrutement in user_groupes:
+            grp_correspondants_rh in user_groupes or \
+            grp_drh in user_groupes or \
+            grp_drh2 in user_groupes or \
+            grp_directeurs_bureau in user_groupes or \
+            grp_administrateurs in user_groupes or \
+            grp_haute_direction in user_groupes:
             return True
 
         if obj is not None:
@@ -532,24 +586,25 @@ class ProxyCandidatAdmin(CandidatAdmin):
     def get_actions(self, request):
         return None
 
+
 class CandidatPieceAdmin(admin.ModelAdmin):
     list_display = ('nom', 'candidat', )
 
     ### Queryset
     def queryset(self, request):
         """
-        Spécifie un queryset limité, autrement Django exécute un 
-        select_related() sans paramètre, ce qui a pour effet de charger tous 
-        les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les 
-        modèles de Region, il existe plusieurs boucles, ce qui conduit à la 
-        génération d'une requête infinie.
-        Affiche la liste de candidats que si le user connecté 
-        possède un Evaluateur
+        Spécifie un queryset limité, autrement Django exécute un
+        select_related() sans paramètre, ce qui a pour effet de charger tous
+        les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
+        modèles de Region, il existe plusieurs boucles, ce qui conduit à la
+        génération d'une requête infinie.  Affiche la liste de candidats que
+        si le user connecté possède un Evaluateur
         """
         qs = self.model._default_manager.get_query_set()
         return qs.select_related('candidat')
 
-class EvaluateurAdmin(VersionAdmin):
+
+class EvaluateurAdmin(BaseAdmin, VersionAdmin):
     fieldsets = (
         ("Utilisateur", {
             'fields': ('user',)
@@ -566,39 +621,42 @@ class EvaluateurAdmin(VersionAdmin):
     def has_add_permission(self, request):
         user_groupes = request.user.groups.all()
         if request.user.is_superuser is True or \
-                grp_drh_recrutement in user_groupes or \
-                grp_drh2_recrutement in user_groupes or \
-                grp_haute_direction_recrutement in user_groupes:
+                grp_drh in user_groupes or \
+                grp_drh2 in user_groupes or \
+                grp_haute_direction in user_groupes:
             return True
-        return False   
+        return False
 
     def has_delete_permission(self, request, obj=None):
         user_groupes = request.user.groups.all()
         if request.user.is_superuser is True or \
-                grp_drh_recrutement in user_groupes or \
-                grp_drh2_recrutement in user_groupes or \
-                grp_haute_direction_recrutement in user_groupes:
+                grp_drh in user_groupes or \
+                grp_drh2 in user_groupes or \
+                grp_haute_direction in user_groupes:
             return True
-        return False   
+        return False
 
     def has_change_permission(self, request, obj=None):
         user_groupes = request.user.groups.all()
         if request.user.is_superuser is True or \
-                grp_drh_recrutement in user_groupes or \
-                grp_drh2_recrutement in user_groupes or \
-                grp_haute_direction_recrutement in user_groupes:
+                grp_drh in user_groupes or \
+                grp_drh2 in user_groupes or \
+                grp_haute_direction in user_groupes:
             return True
-        return False   
+        return False
+
 
-class CandidatEvaluationAdmin(admin.ModelAdmin):
-    search_fields = ('candidat__nom', 'candidat__prenom' )
-    list_display = ('_candidat', '_statut', '_offre_emploi', 'evaluateur', '_note', 
-                    '_commentaire', )
+class CandidatEvaluationAdmin(BaseAdmin):
+    search_fields = ('candidat__nom', 'candidat__prenom')
+    list_display = (
+        '_candidat', '_statut', '_offre_emploi', 'evaluateur', '_note',
+        '_commentaire'
+    )
     readonly_fields = ('candidat', 'evaluateur')
     list_filter = ('candidat__statut', 'candidat__offre_emploi',)
     fieldsets = (
         ('Évaluation du candidat', {
-            'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )        
+            'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
         }),
     )
 
@@ -622,32 +680,33 @@ class CandidatEvaluationAdmin(admin.ModelAdmin):
         """
         page = self.model.__name__.lower()
         redirect_url = 'admin:recrutement_%s_change' % page
-        
+
         if obj.note is None:
             label = "Candidat non évalué"
         else:
             label = obj.note
 
         if self.evaluateur == obj.evaluateur:
-            return "<a href='%s'>%s</a>" % (reverse(redirect_url,  args=(obj.id,)), label)
+            return "<a href='%s'>%s</a>" % (
+                reverse(redirect_url,  args=(obj.id,)), label
+            )
         else:
             return label
     _note.allow_tags = True
-    _note.short_description = "Note"    
-    _note.admin_order_field = 'note'    
+    _note.short_description = "Note"
+    _note.admin_order_field = 'note'
 
     def _statut(self, obj):
         return obj.candidat.get_statut_display()
     _statut.order_field = 'candidat__statut'
     _statut.short_description = 'Statut'
 
-
     ### Lien en lecture seule vers le candidat
     def _candidat(self, obj):
         return "<a href='%s'>%s</a>" \
-            % (reverse('admin:recrutement_proxycandidat_change', 
+            % (reverse('admin:recrutement_proxycandidat_change',
                         args=(obj.candidat.id,)), obj.candidat)
-    _candidat.allow_tags = True    
+    _candidat.allow_tags = True
     _candidat.short_description = 'Candidat'
 
     ### Afficher commentaire
@@ -661,16 +720,16 @@ class CandidatEvaluationAdmin(admin.ModelAdmin):
             return "Aucun"
         return obj.commentaire
     _commentaire.allow_tags = True
-    _commentaire.short_description = "Commentaire"   
+    _commentaire.short_description = "Commentaire"
 
     ### Afficher offre d'emploi
     def _offre_emploi(self, obj):
         return "<a href='%s'>%s</a>" % \
-        (reverse('admin:recrutement_proxyoffreemploi_change', 
+        (reverse('admin:recrutement_proxyoffreemploi_change',
             args=(obj.candidat.offre_emploi.id,)), obj.candidat.offre_emploi)
     _offre_emploi.allow_tags = True
     _offre_emploi.short_description = "Voir offre d'emploi"
-    
+
     def has_add_permission(self, request):
         return False
 
@@ -686,12 +745,12 @@ class CandidatEvaluationAdmin(admin.ModelAdmin):
         user_groupes = request.user.groups.all()
 
         if request.user.is_superuser or \
-            grp_drh_recrutement in user_groupes or \
-            grp_drh2_recrutement in user_groupes or \
-            grp_correspondants_rh_recrutement in user_groupes or \
-            grp_directeurs_bureau_recrutement in user_groupes or \
-            grp_administrateurs_recrutement in user_groupes or \
-            grp_haute_direction_recrutement in user_groupes:
+            grp_drh in user_groupes or \
+            grp_drh2 in user_groupes or \
+            grp_correspondants_rh in user_groupes or \
+            grp_directeurs_bureau in user_groupes or \
+            grp_administrateurs in user_groupes or \
+            grp_haute_direction in user_groupes:
             is_recrutement = True
         else:
             is_recrutement = False
@@ -700,21 +759,22 @@ class CandidatEvaluationAdmin(admin.ModelAdmin):
 
     def queryset(self, request):
         """
-        Afficher uniquement les évaluations de l'évaluateur, sauf si 
+        Afficher uniquement les évaluations de l'évaluateur, sauf si
         l'utilisateur est dans les groupes suivants.
         """
-        qs = self.model._default_manager.get_query_set().select_related('offre_emploi')
+        qs = self.model._default_manager.get_query_set() \
+                .select_related('offre_emploi')
         user_groupes = request.user.groups.all()
 
-        if grp_drh_recrutement in user_groupes or \
-            grp_drh2_recrutement in user_groupes or \
-            grp_correspondants_rh_recrutement in user_groupes or \
-            grp_directeurs_bureau_recrutement in user_groupes or \
-            grp_administrateurs_recrutement in user_groupes or \
-            grp_haute_direction_recrutement in user_groupes:
+        if grp_drh in user_groupes or \
+            grp_drh2 in user_groupes or \
+            grp_correspondants_rh in user_groupes or \
+            grp_directeurs_bureau in user_groupes or \
+            grp_administrateurs in user_groupes or \
+            grp_haute_direction in user_groupes:
             return qs
 
-        evaluateur = Evaluateur.objects.get(user=request.user) 
+        evaluateur = Evaluateur.objects.get(user=request.user)
         candidats_evaluations = \
             CandidatEvaluation.objects.filter(evaluateur=evaluateur,
                     candidat__statut__in=('REC', ))
@@ -740,8 +800,9 @@ class MesCandidatEvaluationAdmin(CandidatEvaluationAdmin):
             return False
 
     def queryset(self, request):
-        qs = self.model._default_manager.get_query_set().select_related('offre_emploi')
-        evaluateur = Evaluateur.objects.get(user=request.user) 
+        qs = self.model._default_manager.get_query_set() \
+                .select_related('offre_emploi')
+        evaluateur = Evaluateur.objects.get(user=request.user)
         candidats_evaluations = \
             CandidatEvaluation.objects.filter(evaluateur=evaluateur,
                     candidat__statut__in=('REC', ))
@@ -749,7 +810,7 @@ class MesCandidatEvaluationAdmin(CandidatEvaluationAdmin):
         return qs.filter(id__in=candidats_evaluations_ids)
 
 
-class CourrielTemplateAdmin(VersionAdmin):
+class CourrielTemplateAdmin(BaseAdmin, VersionAdmin):
     ### Actions à afficher
     def get_actions(self, request):
         actions = super(CourrielTemplateAdmin, self).get_actions(request)