Création à la volée des évaluations à chaque fois qu'on affiche la liste
[auf_rh_dae.git] / project / recrutement / admin.py
index 979c46b..c2b14aa 100644 (file)
 # -*- encoding: utf-8 -*-
 
-from django.core.urlresolvers import reverse
-from django.http import HttpResponseRedirect
+import textwrap
+
+from auf.django.emploi.models import CandidatPiece, Candidat, OffreEmploi
+from auf.django.references.models import Region, Bureau, Implantation
+from django.conf import settings
 from django.contrib import admin
-from django.shortcuts import get_object_or_404
-from django.core.files.storage import default_storage
+from django.core.urlresolvers import reverse
+from django.db.models import Avg
+from django.shortcuts import render_to_response
+from django.template import RequestContext
 
-from reversion.admin import VersionAdmin
-from datamaster_modeles.models import Employe, Implantation, Region
+from auf.django.export.admin import ExportAdmin
+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 recrutement.models import *
-from recrutement.workflow import grp_administrateurs_recrutement,\
-                            grp_evaluateurs_recrutement, grp_drh_recrutement
-from recrutement.forms import *
+from project import groups
+from project.permissions import get_user_groupnames
 
-"""
-class MetaAdmin(VersionAdmin):
-    def get_actions(self, request):
-    
-Pour refactoring
-"""
-class OffreEmploiAdmin(VersionAdmin):
+from project.rh import models as rh
+from project.recrutement.forms import OffreEmploiForm
+from project.recrutement.models import \
+        Evaluateur, CandidatEvaluation, \
+        ProxyOffreEmploi, ProxyCandidat, MesCandidatEvaluation, \
+        CourrielTemplate, OffreEmploiEvaluateur
+
+
+### CONSTANTES
+IMPLANTATIONS_CENTRALES = [15, 19]
+
+
+class BaseAdmin(admin.ModelAdmin):
+
+    class Media:
+        css = {'screen': (
+            'css/admin_custom.css',
+            'jquery-autocomplete/jquery.autocomplete.css',
+        )}
+        js = (
+            'https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js',
+            'jquery-autocomplete/jquery.autocomplete.min.js',
+        )
+
+
+class OrderedChangeList(admin.views.main.ChangeList):
+    """
+    Surcharge pour appliquer le order_by d'un annotate
+    """
+    def get_query_set(self):
+        qs = super(OrderedChangeList, self).get_query_set()
+        qs = qs.order_by('-moyenne')
+        return qs
+
+
+class OffreEmploiAdminMixin(BaseAdmin):
     date_hierarchy = 'date_creation'
-    list_display = ('nom', 'resume', 'date_limite', 'region',  'statut', 
-                    'est_affiche', '_candidatsList')
-    list_filter = ('statut', 'est_affiche', )
+    list_display = (
+        'nom', 'date_limite', 'region',  'statut', 'est_affiche',
+        '_candidatsList'
+    )
+    exclude = ('actif', 'poste_nom', 'resume',)
+    list_filter = ('statut',)
     actions = ['affecter_evaluateurs_offre_emploi', ]
     form = OffreEmploiForm
+    fieldsets = (
+        (None, {
+            'fields': (
+                'est_affiche',
+                'statut',
+                'date_limite',
+                'nom',
+                'description',
+                'poste',
+                'region',
+                'lieu_affectation',
+                'bureau',
+                'debut_affectation',
+                'duree_affectation',
+                'renumeration',
+            )
+        }),
+    )
 
+    ### Actions à afficher
     def get_actions(self, request):
-        actions = super(OffreEmploiAdmin, self).get_actions(request)
+        actions = super(OffreEmploiAdminMixin, self).get_actions(request)
         del actions['delete_selected']
         return actions
 
-    # Affecter un évaluateurs à des offres d'emploi
-    def affecter_evaluateurs_offre_emploi(modeladmin, obj, candidats):   
+    ### Affecter un évaluateurs à des offres d'emploi
+    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))
+        )
 
-    # Afficher la liste des candidats pour l'offre d'emploi
-    def _candidatsList(self, obj):     
+    affecter_evaluateurs_offre_emploi.short_description = \
+            u'Affecter évaluateur(s)'
+
+    ### Afficher la liste des candidats pour l'offre d'emploi
+    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 
+            </a>" % (reverse('admin:recrutement_proxycandidat_changelist'), obj.id)
+    _candidatsList.allow_tags = True
     _candidatsList.short_description = "Afficher la liste des candidats"
 
+    ### Formulaire
+    def get_form(self, request, obj=None, **kwargs):
+        form = super(OffreEmploiAdminMixin, self).get_form(request, obj, **kwargs)
+        employe = groups.get_employe_from_user(request.user)
+        user_groupes = get_user_groupnames(request.user)
+
+        # Region
+        region_field = None
+        if 'region' in form.declared_fields.keys():
+            region_field = form.declared_fields['region']
+        if 'region' in form.base_fields.keys():
+            region_field = form.base_fields['region']
+        if region_field:
+            if groups.DRH_NIVEAU_1 in user_groupes or \
+               groups.DRH_NIVEAU_2 in user_groupes or \
+               groups.HAUTE_DIRECTION in user_groupes:
+                region_field.queryset = Region.objects.all()
+            else:
+                region_field.queryset = Region.objects.\
+                                    filter(id=employe.implantation.region.id)
+
+        # Poste
+        poste_field = None
+        if 'poste' in form.declared_fields.keys():
+            poste_field = form.declared_fields['poste']
+        if 'poste' in form.base_fields.keys():
+            poste_field = form.base_fields['poste']
+        if poste_field:
+            if groups.DRH_NIVEAU_1 in user_groupes or \
+               groups.DRH_NIVEAU_2 in user_groupes or \
+               groups.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
+        bureau_field = None
+        if 'bureau' in form.declared_fields.keys():
+            bureau_field = form.declared_fields['bureau']
+        if 'bureau' in form.base_fields.keys():
+            bureau_field = form.base_fields['bureau']
+        if bureau_field:
+            if groups.DRH_NIVEAU_1 in user_groupes or \
+                groups.DRH_NIVEAU_2 in user_groupes or \
+                groups.HAUTE_DIRECTION in user_groupes:
+                bureau_field.queryset = Bureau.objects.all()
+            else:
+                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()
-        # Si user est superuser afficher toutes les offres d'emploi  
-        user_groupes = request.user.groups.all()
-        if not grp_drh_recrutement in user_groupes and \
-            not request.user.is_superuser:
-            """
-            Si le user n'est ni un évaluateur ni un administrateur régional,
-            retourner none
-            Vérifier groupes
-            """
-            if grp_evaluateurs_recrutement in user_groupes:
-                try:
-                    user = Evaluateur.objects.get(user=request.user)               
-                except Evaluateur.DoesNotExist:       
-                    return qs.none()     
-            elif grp_administrateurs_recrutement in user_groupes: 
-                try:
-                    user = AdministrateurRegional.objects.get(user=request.user)
-                except AdministrateurRegional.DoesNotExist:       
-                    return qs.none()     
-            else:
-                return qs.none()   
-                      
-            if type(user) is AdministrateurRegional:
-                region_ids = [g.id for g in user.regions.all()]
-                return qs.select_related('offre_emploi').\
-                        filter(region__in=region_ids)
-            if type(user) is Evaluateur:        
-                candidats = [g for g in user.candidats.all()]
-                offre_emploi_ids = [c.offre_emploi.id for c in candidats]
-                return qs.select_related('offre_emploi').\
-                        filter(id__in=offre_emploi_ids)            
-            return qs.none()
-        return qs.select_related('offre_emploi')
+        qs = self.model._default_manager.get_query_set() \
+                .select_related('offre_emploi')
+        user_groupes = get_user_groupnames(request.user)
+        if groups.DRH_NIVEAU_1 in user_groupes or \
+            groups.DRH_NIVEAU_2 in user_groupes or \
+            groups.HAUTE_DIRECTION in user_groupes:
+            return qs
+
+        if groups.DIRECTEUR_DE_BUREAU in user_groupes or \
+            groups.CORRESPONDANT_RH in user_groupes or \
+            groups.ADMINISTRATEURS in user_groupes:
+            employe = groups.get_employe_from_user(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)
+            ]
+            return qs.filter(id__in=offre_ids)
+
+        return qs.none()
+
+    ### Permission add, delete, change
+    def has_add_permission(self, request):
+        user_groupes = get_user_groupnames(request.user)
+        if request.user.is_superuser is True or \
+            groups.DRH_NIVEAU_1 in user_groupes or \
+            groups.DRH_NIVEAU_2 in user_groupes or \
+            groups.DIRECTEUR_DE_BUREAU in user_groupes or \
+            groups.ADMINISTRATEURS in user_groupes or \
+            groups.HAUTE_DIRECTION in user_groupes:
+            return True
+        return False
+
+    def has_delete_permission(self, request, obj=None):
+        user_groupes = get_user_groupnames(request.user)
+        if request.user.is_superuser is True or \
+            groups.DRH_NIVEAU_1 in user_groupes or \
+            groups.DRH_NIVEAU_2 in user_groupes or \
+            groups.HAUTE_DIRECTION in user_groupes:
+            return True
+
+        if obj is not None:
+            employe = groups.get_employe_from_user(request.user)
+            if (groups.DIRECTEUR_DE_BUREAU in user_groupes or \
+                groups.ADMINISTRATEURS in user_groupes) and ( 
+                employe.implantation.region == obj.lieu_affectation.region):
+                return True
+
+        return False
 
     def has_change_permission(self, request, obj=None):
-        user_groupes = request.user.groups.all()
-        if grp_drh_recrutement in user_groupes or \
-            grp_administrateurs_recrutement in user_groupes or \
-            request.user.is_superuser:
+        user_groupes = get_user_groupnames(request.user)
+        if request.user.is_superuser is True or \
+            groups.DRH_NIVEAU_1 in user_groupes or \
+            groups.DRH_NIVEAU_2 in user_groupes or \
+            groups.HAUTE_DIRECTION in user_groupes:
             return True
-        return False   
-
-class ProxyOffreEmploiAdmin(OffreEmploiAdmin):
-    list_display = ('nom', 'resume', 'date_limite', 'region', 'statut', 
-                    'est_affiche')
-    readonly_fields = ('description', 'bureau',
-                        'duree_affectation', 'renumeration',
-                        'debut_affectation', 'lieu_affectation', 'nom',
-                        'resume', 'date_limite', 'region')
+
+        if obj is not None:
+            employe = groups.get_employe_from_user(request.user)
+            if (groups.DIRECTEUR_DE_BUREAU in user_groupes or \
+                groups.ADMINISTRATEURS in user_groupes) and ( 
+                employe.implantation.region == obj.lieu_affectation.region):
+                return True
+        else:
+            if  groups.DIRECTEUR_DE_BUREAU in user_groupes or \
+                    groups.ADMINISTRATEURS in user_groupes:
+                return True
+
+
+        return False
+
+    def formfield_for_foreignkey(self, db_field, request, **kwargs):
+        if db_field.name == 'lieu_affectation':
+            user_groupes = [g.name for g in request.user.groups.all()]
+            if not (request.user.is_superuser is True or \
+                groups.DRH_NIVEAU_1 in user_groupes or \
+                groups.DRH_NIVEAU_2 in user_groupes):
+                employe = groups.get_employe_from_user(request.user)
+                kwargs["queryset"] = Implantation.objects.filter(region=employe.implantation.region)
+            return db_field.formfield(**kwargs)
+        return super(OffreEmploiAdminMixin, self).formfield_for_foreignkey(db_field, request, **kwargs)
+
+
+class OffreEmploiAdmin(VersionAdmin, OffreEmploiAdminMixin):
+    pass
+
+
+class ProxyOffreEmploiAdmin(OffreEmploiAdminMixin):
+    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': ('resume','description', 'date_limite', )        
+            'fields': ('description', 'date_limite',)
         }),
         ('Coordonnées', {
-            'fields': ('lieu_affectation', 'bureau', 'region', )
+            '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
     def response_change(self, request, obj):
-        response = super(ProxyOffreEmploiAdmin, self).response_change(request, obj)
-        user_groupes = request.user.groups.all()
-        if grp_drh_recrutement in user_groupes or \
-            request.user.is_superuser:
-            return HttpResponseRedirect(reverse('admin:recrutement_offreemploi_changelist'))
-        return HttpResponseRedirect(reverse('admin:recrutement_proxyoffreemploi_changelist'))
+        return redirect('admin:recrutement_proxyoffreemploi_changelist')
 
+    ### Permissions add, delete, change
     def has_add_permission(self, request):
         return False
 
@@ -132,35 +299,32 @@ class ProxyOffreEmploiAdmin(OffreEmploiAdmin):
         return False
 
     def has_change_permission(self, request, obj=None):
-        user_groupes = request.user.groups.all()
-        if grp_evaluateurs_recrutement in user_groupes or \
-            grp_drh_recrutement in user_groupes or \
-            request.user.is_superuser:
+        if obj is not None:
             return True
-        return False   
 
-    """class ProxyEvaluateur(Evaluateur.candidats.through):
+        return not super(ProxyOffreEmploiAdmin, self).has_change_permission(request, obj)
 
-    Ce proxy sert uniquement dans l'admin à disposer d'un libellé
-    plus ergonomique.
 
-    class Meta:
-        proxy = True
-        verbose_name = "évaluateur"
-    """
 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
+
+
 class CandidatEvaluationInlineFormSet(BaseInlineFormSet):
     """
     Empêche la suppression d'une évaluation pour le CandidatEvaluationInline
     """
     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
@@ -168,38 +332,48 @@ class CandidatEvaluationInline(admin.TabularInline):
     max_num = 0
     extra = 0
     formset = CandidatEvaluationInlineFormSet
-    
+
+    ### 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):
-    date_hierarchy = 'date_creation'
-    list_display = ('nom', 'prenom', 'offre_emploi','statut',
-                    'voir_offre_emploi', 'calculer_moyenne', 
-                    'afficher_candidat',)
-    list_filter = ('offre_emploi', )
+class CandidatAdminMixin(BaseAdmin, ExportAdmin):
+    search_fields = ('nom', 'prenom')
+    exclude = ('actif', )
+    list_editable = ('statut', )
+    list_display = ('_candidat', 'offre_emploi',
+                    'voir_offre_emploi', 'calculer_moyenne',
+                    'afficher_candidat', '_date_creation', 'statut', )
+    list_filter = ('offre_emploi__nom', 'offre_emploi__region', 'statut', )
+
     fieldsets = (
         ("Offre d'emploi", {
             'fields': ('offre_emploi', )
         }),
         ('Informations personnelles', {
-            'fields': ('prenom','nom','genre', 'nationalite',
-                        'situation_famille', 'nombre_dependant',)        
+            'fields': (
+                'nom', 'prenom', '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', )
         }),
@@ -208,152 +382,248 @@ class CandidatAdmin(VersionAdmin):
         CandidatPieceInline,
         CandidatEvaluationInline,
     ]
+    actions = ['envoyer_courriel_candidats', 'changer_statut']
 
-    actions = ['envoyer_courriel_candidats']
+    export_fields = ['statut', 'offre_emploi', 'prenom', 'nom', 'genre',
+                     'nationalite', 'situation_famille', 'nombre_dependant',
+                     'niveau_diplome', 'employeur_actuel', 'poste_actuel',
+                     'domaine_professionnel', 'telephone', 'email', 'adresse',
+                     'ville', 'etat_province', 'code_postal', 'pays']
 
+    def _candidat(self, obj):
+        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"
+    _candidat.admin_order_field = "nom"
+    _candidat.allow_tags = True
+
+    def _date_creation(self, obj):
+        return obj.date_creation
+    _date_creation.admin_order_field = "date_creation"
+    _date_creation.short_description = "Date de réception"
+
+    ### Actions à afficher
     def get_actions(self, request):
-        actions = super(CandidatAdmin, self).get_actions(request)
+        actions = super(CandidatAdminMixin, self).get_actions(request)
         del actions['delete_selected']
         return actions
 
-    # Envoyer un courriel à des candidats
-    def envoyer_courriel_candidats(modeladmin, obj, candidats):   
+    ### Envoyer un courriel à des 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'
 
-    # Évaluer un candidat
+    ### Changer le statut à des candidats
+    def changer_statut(modeladmin, request, queryset):
+        if request.POST.get('post'):
+            queryset.update(statut=request.POST.get('statut'))
+            return None
+
+        context = {
+            'action_checkbox_name': admin.helpers.ACTION_CHECKBOX_NAME,
+            'queryset': queryset,
+            'status': STATUT_CHOICES,
+        }
+
+        return render_to_response("recrutement/selectionner_statut.html",
+                context, context_instance = RequestContext(request))
+
+    changer_statut.short_description = u'Changer statut'
+
+    ### É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
+    ### Afficher un candidat
     def afficher_candidat(self, obj):
-        return "<a href='%s'>Voir le candidat</a>" % \
-            (reverse('admin:recrutement_proxycandidat_change', args=(obj.id,)))
-    afficher_candidat.allow_tags = True    
+        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'>Candidature</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.short_description = u'Détails du candidat'
 
-    # Voir l'offre d'emploi
+    ### 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"
 
-    # Calculer la moyenne des notes
+    ### Calculer la moyenne des notes
     def calculer_moyenne(self, obj):
         evaluations = CandidatEvaluation.objects.filter(candidat=obj)
-        offre_emploi = obj.offre_emploi
 
-        notes = [evaluation.note for evaluation in evaluations.all() \
+        notes = [evaluation.note for evaluation in evaluations \
                     if evaluation.note is not None]
-        if len(notes) > 0 and offre_emploi.date_limite <= datetime.date.today():
-            moyenne_votes = float(sum(notes)) / len(notes)
+
+        if len(notes) > 0:
+            moyenne_votes = round(float(sum(notes)) / len(notes), 2)
         else:
             moyenne_votes = "Non disponible"
-        return moyenne_votes
-    calculer_moyenne.allow_tags = True
-    calculer_moyenne.short_description = "Moyenne des notes"
 
-    def add_delete_permission(self, request, obj=None) :
-        user_groupes = request.user.groups.all()
-        if grp_drh_recrutement in user_groupes or \
-            grp_administrateurs_recrutement in user_groupes or \
-            request.user.is_superuser:
-            return True
-        return False    
+        totales = len(evaluations)
+        faites = len(notes)
 
+        if obj.statut == 'REC':
+            if totales == faites:
+                color = "green"
+            elif faites > 0 and float(totales) / float(faites) >= 2:
+                color = "orange"
+            else:
+                color = "red"
+        else:
+            color = "black"
+
+        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 = ""
+
+    ### Permissions add, delete, change
     def has_add_permission(self, request):
-        return self.add_delete_permission(request, request)
+        user_groupes = get_user_groupnames(request.user)
+        if request.user.is_superuser is True or \
+            groups.CORRESPONDANT_RH in user_groupes or \
+            groups.DRH_NIVEAU_1 in user_groupes or \
+            groups.DRH_NIVEAU_2 in user_groupes or \
+            groups.DIRECTEUR_DE_BUREAU in user_groupes or \
+            groups.ADMINISTRATEURS in user_groupes or \
+            groups.HAUTE_DIRECTION in user_groupes:
+            return True
+        return False
 
     def has_delete_permission(self, request, obj=None):
-        return self.add_delete_permission(request, request)
+        user_groupes = get_user_groupnames(request.user)
+        if request.user.is_superuser is True or \
+            groups.CORRESPONDANT_RH in user_groupes or \
+            groups.DRH_NIVEAU_1 in user_groupes or \
+            groups.DRH_NIVEAU_2 in user_groupes or \
+            groups.DIRECTEUR_DE_BUREAU in user_groupes or \
+            groups.ADMINISTRATEURS in user_groupes or \
+            groups.HAUTE_DIRECTION in user_groupes:
+            return True
+        return False
 
     def has_change_permission(self, request, obj=None):
-        user_groupes = request.user.groups.all()
-        if grp_drh_recrutement in user_groupes or \
-            grp_administrateurs_recrutement in user_groupes or \
-            request.user.is_superuser:
+        user_groupes = get_user_groupnames(request.user)
+        if request.user.is_superuser is True or \
+            groups.CORRESPONDANT_RH in user_groupes or \
+            groups.DRH_NIVEAU_1 in user_groupes or \
+            groups.DRH_NIVEAU_2 in user_groupes or \
+            groups.DIRECTEUR_DE_BUREAU in user_groupes or \
+            groups.ADMINISTRATEURS in user_groupes or \
+            groups.HAUTE_DIRECTION in user_groupes:
             return True
-        return False   
+        return False
+
+    def formfield_for_foreignkey(self, db_field, request, **kwargs):
+        if db_field.name == 'offre_emploi':
+            employe = groups.get_employe_from_user(request.user)
+            user_groupes = [g.name for g in request.user.groups.all()]
+            if request.user.is_superuser is True or \
+                groups.CORRESPONDANT_RH in user_groupes or \
+                groups.DRH_NIVEAU_1 in user_groupes or \
+                groups.DRH_NIVEAU_2 in user_groupes:
+                qs_offres = OffreEmploi.objects.all()
+            else:
+                qs_offres =OffreEmploi.objects.filter(region=employe.implantation.region)
+            kwargs["queryset"] = qs_offres
+            return db_field.formfield(**kwargs)
+        return super(CandidatAdminMixin, self).formfield_for_foreignkey(db_field, request, **kwargs)
+
+    def get_changelist(self, request, **kwargs):
+        return OrderedChangeList
 
     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() 
-        # Si user est superuser afficher tous les candidats       
-        user_groupes = request.user.groups.all()
-        if not grp_drh_recrutement in user_groupes and \
-            not request.user.is_superuser:
-            # Si le user n'est ni un évaluateur ni un administrateur régional,
-            # retourner none
-
-            # Vérifier groupes
-            if grp_evaluateurs_recrutement in user_groupes:
-                try:
-                    user = Evaluateur.objects.get(user=request.user)
-                except Evaluateur.DoesNotExist:       
-                    return qs.none()       
-                """
-                elif grp_administrateurs_recrutement in user_groupes: 
-                    try:
-                        user = AdministrateurRegional.objects.get(user=obj.user)
-                    except AdministrateurRegional.DoesNotExist:       
-                        return qs.none()    
-                """
-            else:
-                return qs.none()   
-            ids = [c.id for c in user.candidats.all()]
-            return qs.select_related('candidats').filter(id__in=ids)               
-        return qs.select_related('candidats')         
-
-class ProxyCandidatAdmin(CandidatAdmin):
-    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',)
+        qs = self.model._default_manager.get_query_set() \
+                .select_related('offre_emploi') \
+                .annotate(moyenne=Avg('evaluations__note'))
+
+        user_groupes = get_user_groupnames(request.user)
+        if groups.DRH_NIVEAU_1 in user_groupes or \
+            groups.DRH_NIVEAU_2 in user_groupes or \
+            groups.HAUTE_DIRECTION in user_groupes:
+            return qs
+
+        if groups.DIRECTEUR_DE_BUREAU in user_groupes or \
+            groups.CORRESPONDANT_RH in user_groupes or \
+            groups.ADMINISTRATEURS in user_groupes:
+            employe = groups.get_employe_from_user(request.user)
+            return qs.filter(offre_emploi__region=employe.implantation.region)
+
+        if  Evaluateur.objects.filter(user=request.user).exists():
+            evaluateur = Evaluateur.objects.get(user=request.user)
+            candidat_ids = [e.candidat.id for e in
+                    CandidatEvaluation.objects.filter(evaluateur=evaluateur)]
+            return qs.filter(id__in=candidat_ids)
+        return qs.none()
+
+
+class CandidatAdmin(VersionAdmin, CandidatAdminMixin):
+    change_list_template = 'admin/recrutement/candidat/change_list.html'
+    pass
+
+
+class ProxyCandidatAdmin(CandidatAdminMixin):
+    change_list_template = 'admin/recrutement/candidat/change_list.html'
+    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'
+    )
     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 = []
-
-    def response_change(self, request, obj):
-        response = super(ProxyCandidatAdmin, self).response_change(request, obj)
-        user_groupes = request.user.groups.all()
-        if grp_drh_recrutement in user_groupes or \
-            request.user.is_superuser:
-            return HttpResponseRedirect(reverse('admin:recrutement_candidat_changelist'))
-        return HttpResponseRedirect(reverse('admin:recrutement_proxycandidat_changelist'))
+    inlines = (CandidatEvaluationInline, )
 
     def has_add_permission(self, request):
         return False
@@ -362,92 +632,145 @@ class ProxyCandidatAdmin(CandidatAdmin):
         return False
 
     def has_change_permission(self, request, obj=None):
-        user_groupes = request.user.groups.all()
-        if grp_drh_recrutement in user_groupes or \
-            grp_administrateurs_recrutement in user_groupes or \
-            grp_evaluateurs_recrutement in user_groupes or \
-            request.user.is_superuser:
-            return True
-        return False   
+        if obj is not None:
+            return obj in self.queryset(request)
+            #try:
+            #    evaluateur = Evaluateur.objects.get(user=request.user)
+            #    for e in obj.evaluations.all():
+            #        if e.evaluateur == evaluateur:
+            #            return True
+            #    return False
+            #except:
+            #    pass
+        return super(ProxyCandidatAdmin, self).has_change_permission(request, obj)
+
+    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',)
         }),
-        ("Offres d'emploi à évaluer", {
-            'fields': ('offres_emploi',)
-        }),
     )
 
+    ### Actions à afficher
     def get_actions(self, request):
         actions = super(EvaluateurAdmin, self).get_actions(request)
         del actions['delete_selected']
         return actions
 
-class AdministrateurRegionalAdmin(VersionAdmin):
-    pass
+    ### Permissions add, delete, change
+    def has_add_permission(self, request):
+        user_groupes = get_user_groupnames(request.user)
+        if request.user.is_superuser is True or \
+                groups.DRH_NIVEAU_1 in user_groupes or \
+                groups.DRH_NIVEAU_2 in user_groupes or \
+                groups.HAUTE_DIRECTION in user_groupes:
+            return True
+        return False
+
+    def has_delete_permission(self, request, obj=None):
+        user_groupes = get_user_groupnames(request.user)
+        if request.user.is_superuser is True or \
+                groups.DRH_NIVEAU_1 in user_groupes or \
+                groups.DRH_NIVEAU_2 in user_groupes or \
+                groups.HAUTE_DIRECTION in user_groupes:
+            return True
+        return False
+
+    def has_change_permission(self, request, obj=None):
+        user_groupes = get_user_groupnames(request.user)
+        if request.user.is_superuser is True or \
+                groups.DRH_NIVEAU_1 in user_groupes or \
+                groups.DRH_NIVEAU_2 in user_groupes or \
+                groups.HAUTE_DIRECTION in user_groupes:
+            return True
+        return False
 
-class CandidatEvaluationAdmin(VersionAdmin):
-    list_display = ('_candidat', '_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', )
         }),
     )
 
     def get_actions(self, request):
+        # on stocke l'evaluateur connecté (pas forcément la meilleure place...)
+        try:
+            self.evaluateur = Evaluateur.objects.get(user=request.user)
+        except:
+            self.evaluateur = None
+
         actions = super(CandidatEvaluationAdmin, self).get_actions(request)
         del actions['delete_selected']
         return actions
 
+    ### Afficher la note
     def _note(self, obj):
         """
         Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
         un lien pour Évaluer le candidat.
         Sinon afficher la note.
         """
-        evaluateur = obj.evaluateur
-        candidat = obj.candidat
-        candidat_evaluation = CandidatEvaluation.objects.\
-                                get(candidat=candidat, evaluateur=evaluateur)
+        page = self.model.__name__.lower()
+        redirect_url = 'admin:recrutement_%s_change' % page
+
         if obj.note is None:
-            return "<a href='%s'>Candidat non évalué</a>" % \
-                (reverse('admin:recrutement_candidatevaluation_change', 
-                args=(candidat_evaluation.id,)))
-        return "<a href='%s'>%s</a>" % \
-            (reverse('admin:recrutement_candidatevaluation_change', 
-            args=(candidat_evaluation.id,)), obj.note)
-        return 
+            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
+            )
+        else:
+            return label
     _note.allow_tags = True
-    _note.short_description = "Votre 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
     def _commentaire(self, obj):
         """
         Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
@@ -458,48 +781,114 @@ class CandidatEvaluationAdmin(VersionAdmin):
             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
+
+    def has_delete_permission(self, request, obj=None):
+        return False
+
     def has_change_permission(self, request, obj=None):
         """
         Permettre la visualisation dans la changelist
         mais interdire l'accès à modifier l'objet si l'évaluateur n'est pas
         le request.user
         """
-        return obj is None or request.user == obj.evaluateur.user
+        user_groupes = get_user_groupnames(request.user)
+
+        if request.user.is_superuser or \
+            groups.CORRESPONDANT_RH in user_groupes or \
+            groups.DRH_NIVEAU_1 in user_groupes or \
+            groups.DRH_NIVEAU_2 in user_groupes or \
+            groups.DIRECTEUR_DE_BUREAU in user_groupes or \
+            groups.ADMINISTRATEURS in user_groupes or \
+            groups.HAUTE_DIRECTION in user_groupes:
+            is_recrutement = True
+        else:
+            is_recrutement = False
+
+        return is_recrutement
 
     def queryset(self, request):
         """
-        Afficher uniquement les évaluations de l'évaluateur, sauf si 
-        l'utilisateur est super admin.
+        Afficher uniquement les évaluations de l'évaluateur, sauf si
+        l'utilisateur est dans les groupes suivants.
         """
-        qs = self.model._default_manager.get_query_set()
-        user_groupes = request.user.groups.all()
-        if grp_drh_recrutement in user_groupes  or \
-            request.user.is_superuser:
-            return qs.select_related('offre_emploi')
+        qs = self.model._default_manager.get_query_set() \
+                .select_related('offre_emploi')
+        user_groupes = get_user_groupnames(request.user)
+
+        if request.user.is_superuser or \
+            groups.CORRESPONDANT_RH in user_groupes or \
+            groups.DRH_NIVEAU_1 in user_groupes or \
+            groups.DRH_NIVEAU_2 in user_groupes or \
+            groups.DIRECTEUR_DE_BUREAU in user_groupes or \
+            groups.ADMINISTRATEURS in user_groupes or \
+            groups.HAUTE_DIRECTION in user_groupes:
+            return qs.filter(candidat__statut__in=('REC', 'SEL'))
+
+        evaluateur = Evaluateur.objects.get(user=request.user)
+        candidats_evaluations = \
+            CandidatEvaluation.objects.filter(evaluateur=evaluateur,
+                    candidat__statut__in=('REC', ))
+        candidats_evaluations_ids = [ce.id for ce in candidats_evaluations]
+        return qs.filter(id__in=candidats_evaluations_ids)
+
+
+class MesCandidatEvaluationAdmin(CandidatEvaluationAdmin):
+    list_filter = []
+
+    def has_change_permission(self, request, obj=None):
+        try:
+            Evaluateur.objects.get(user=request.user)
+            is_evaluateur = True
+        except:
+            is_evaluateur = False
+
+        if obj is None and is_evaluateur:
+            return True
 
         try:
-            evaluateur = Evaluateur.objects.get(user=request.user) 
-        except Evaluateur.DoesNotExist:       
-            return qs.none()     
-        
-        candidats_evaluations = CandidatEvaluation.objects.\
-                                filter(evaluateur=evaluateur)
-        candidats_evaluations_ids = [ce.id for ce in \
-                                        candidats_evaluations.all()]
-        return qs.select_related('offre_emploi').\
-                filter(id__in=candidats_evaluations_ids)
-
-class CourrielTemplateAdmin(VersionAdmin):
+            return request.user == obj.evaluateur.user
+        except:
+            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)
+
+        # XXX: Pas l'idéal, mais on doit créer les objets CandidatEvaluation
+        # ici pour garder la liste à jour. Idéalement, il vaudrait peut-être
+        # mieux utiliser directement les objets Candidat.
+        for candidat in Candidat.objects .filter(
+            offre_emploi__offreemploievaluateur__evaluateur=evaluateur,
+        ).exclude(evaluations__evaluateur=evaluateur):
+            print candidat, candidat.offre_emploi
+            CandidatEvaluation.objects.get_or_create(
+                candidat=candidat, evaluateur=evaluateur
+            )
+
+        return qs.filter(
+            evaluateur=evaluateur, candidat__statut__in=('NOUV', 'REC')
+        )
+
+
+class OffreEmploiEvaluateurAdmin(BaseAdmin):
+    pass
+
+
+class CourrielTemplateAdmin(BaseAdmin, VersionAdmin):
+    ### Actions à afficher
     def get_actions(self, request):
         actions = super(CourrielTemplateAdmin, self).get_actions(request)
         del actions['delete_selected']
@@ -510,6 +899,7 @@ admin.site.register(ProxyOffreEmploi, ProxyOffreEmploiAdmin)
 admin.site.register(Candidat, CandidatAdmin)
 admin.site.register(ProxyCandidat, ProxyCandidatAdmin)
 admin.site.register(CandidatEvaluation, CandidatEvaluationAdmin)
+admin.site.register(MesCandidatEvaluation, MesCandidatEvaluationAdmin)
 admin.site.register(Evaluateur, EvaluateurAdmin)
-#admin.site.register(AdministrateurRegional, AdministrateurRegionalAdmin)
 admin.site.register(CourrielTemplate, CourrielTemplateAdmin)
+admin.site.register(OffreEmploiEvaluateur, OffreEmploiEvaluateurAdmin)