X-Git-Url: http://git.auf.org/?p=auf_rh_dae.git;a=blobdiff_plain;f=project%2Frecrutement%2Fadmin.py;h=c2b14aa4bafe9a16b1b7d9cb753bb335fb29a484;hp=e0e435f26223b4d445b0014a2ea4d8be0e6ba6f0;hb=bad00d312daf1bf886ca1d5b2708c1d3e4e91650;hpb=b8b74deecc19496a4875cba101f8110f7b353593 diff --git a/project/recrutement/admin.py b/project/recrutement/admin.py index e0e435f..c2b14aa 100644 --- a/project/recrutement/admin.py +++ b/project/recrutement/admin.py @@ -1,151 +1,297 @@ # -*- 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 -""" +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 ProxyEvaluateur(Evaluateur.offres_emploi.through): + +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): """ - Ce proxy sert uniquement dans l'admin à disposer d'un libellé - plus ergonomique. + Surcharge pour appliquer le order_by d'un annotate """ - class Meta: - proxy = True - verbose_name = "évaluateur" + def get_query_set(self): + qs = super(OrderedChangeList, self).get_query_set() + qs = qs.order_by('-moyenne') + return qs -class EvaluateurInline(admin.TabularInline): - model = ProxyEvaluateur - fields = ('evaluateur',) - extra = 1 -class OffreEmploiAdmin(VersionAdmin): +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 - inlines = [EvaluateurInline, ] + 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 "Voir les candidats \ - " % (reverse('admin:recrutement_candidat_changelist'), obj.id) - _candidatsList.allow_tags = True + " % (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', + ) }), - ) - - def get_actions(self, request): - actions = super(ProxyOffreEmploiAdmin, self).get_actions(request) - del actions['affecter_evaluateurs_offre_emploi'] - return actions + ) + 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 @@ -153,12 +299,11 @@ 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 + + return not super(ProxyOffreEmploiAdmin, self).has_change_permission(request, obj) + class CandidatPieceInline(admin.TabularInline): model = CandidatPiece @@ -166,13 +311,20 @@ class CandidatPieceInline(admin.TabularInline): 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 @@ -180,37 +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', ) }), @@ -219,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 "
".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 "Évaluer le candidat" % \ - (reverse('admin:recrutement_candidatevaluation_changelist'), - obj.id) - evaluer_candidat.allow_tags = True + return "" \ + "Évaluer le candidat" % ( + 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 "Voir le candidat" % \ - (reverse('admin:recrutement_proxycandidat_change', args=(obj.id,))) - afficher_candidat.allow_tags = True + items = [u"
  • %s
  • " % \ + (settings.OE_PRIVE_MEDIA_URL, pj.path, pj.get_nom_display()) \ + for pj in obj.pieces_jointes()] + html = "Candidature" % ( + reverse('admin:recrutement_proxycandidat_change', args=(obj.id,)) + ) + return "%s" % (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 "Voir l'offre d'emploi" % \ - (reverse('admin:recrutement_proxyoffreemploi_change', - args=(obj.offre_emploi.id,))) + return "Voir l'offre d'emploi" % (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 """%s (%s/%s)""" % ( + 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 @@ -373,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 "Candidat non évalué" % \ - (reverse('admin:recrutement_candidatevaluation_change', - args=(candidat_evaluation.id,))) - return "%s" % \ - (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 "%s" % ( + 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 "%s" \ - % (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 @@ -469,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 "%s" % \ - (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'] @@ -521,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)