1 # -*- encoding: utf-8 -*-
3 from django
.core
.urlresolvers
import reverse
4 from django
.http
import HttpResponseRedirect
5 from django
.contrib
import admin
6 from django
.shortcuts
import get_object_or_404
8 from reversion
.admin
import VersionAdmin
9 from datamaster_modeles
.models
import Employe
, Implantation
, Region
11 from recrutement
.models
import *
12 from recrutement
.workflow
import grp_administrateurs_recrutement
,\
13 grp_evaluateurs_recrutement
, grp_drh_recrutement
15 class OffreEmploiAdmin(VersionAdmin
):
16 date_hierarchy
= 'date_creation'
17 list_display
= ('nom', 'resume', 'date_limite', 'region', '_candidatsList')
19 # Afficher la liste des candidats pour l'offre d'emploi
20 def _candidatsList(self
, obj
):
21 return "<a href='%s?offre_emploi__id__exact=%s'>Voir les candidats \
22 </a>" % (reverse('admin:recrutement_candidat_changelist'), obj
.id)
23 _candidatsList
.allow_tags
= True
24 _candidatsList
.short_description
= "Afficher la liste des candidats"
26 def queryset(self
, request
):
27 query
= self
.model
._default_manager
.get_query_set()
28 # Si user est superuser afficher toutes les offres d'emploi
29 user_groupes
= request
.user
.groups
.all()
30 if not grp_drh_recrutement
in user_groupes
:
32 Filtrer les offre d'emploi pour afficher seulement les offres
35 qs
= query
.filter(date_limite__gte
=datetime
.date
.today())
37 Si le user n'est ni un évaluateur ni un administrateur régional,
41 if grp_evaluateurs_recrutement
in user_groupes
:
43 user
= Evaluateur
.objects
.get(user
=request
.user
)
44 except Evaluateur
.DoesNotExist
:
46 elif grp_administrateurs_recrutement
in user_groupes
:
48 user
= AdministrateurRegional
.objects
.get(user
=request
.user
)
49 except AdministrateurRegional
.DoesNotExist
:
54 if type(user
) is AdministrateurRegional
:
55 region_ids
= [g
.id for g
in user
.regions
.all()]
56 return qs
.select_related('offre_emploi').\
57 filter(region__in
=region_ids
)
58 if type(user
) is Evaluateur
:
59 candidats
= [g
for g
in user
.candidats
.all()]
60 offre_emploi_ids
= [c
.offre_emploi
.id for c
in candidats
]
61 return qs
.select_related('offre_emploi').\
62 filter(id__in
=offre_emploi_ids
)
64 return query
.select_related('offre_emploi')
66 def has_change_permission(self
, request
, obj
=None):
67 user_groupes
= request
.user
.groups
.all()
68 if grp_drh_recrutement
in user_groupes
or \
69 grp_administrateurs_recrutement
in user_groupes
:
73 class ProxyOffreEmploiAdmin(OffreEmploiAdmin
):
74 list_display
= ('nom', 'resume', 'date_limite', 'region', )
75 readonly_fields
= ('actif', 'description', 'poste', 'bureau',
76 'duree_affectation', 'renumeration',
77 'debut_affectation', 'lieu_affectation', 'nom',
78 'resume', 'date_limite', 'region')
83 ('Description générale', {
84 'fields': ('poste', 'resume','description', 'date_limite', )
87 'fields': ('lieu_affectation', 'bureau', 'region', )
90 'fields': ('debut_affectation', 'duree_affectation',
94 def has_add_permission(self
, request
):
97 def has_delete_permission(self
, request
, obj
=None):
100 def has_change_permission(self
, request
, obj
=None):
101 user_groupes
= request
.user
.groups
.all()
102 if grp_evaluateurs_recrutement
in user_groupes
or \
103 grp_drh_recrutement
in user_groupes
:
107 class ProxyCandidatPiece(CandidatPiece
):
109 Ce proxy sert uniquement dans l'admin à disposer d'un libellé
114 verbose_name
= "pièce jointe"
115 verbose_name_plural
= "pièces jointes"
117 class CandidatPieceInline(admin
.TabularInline
):
118 model
= ProxyCandidatPiece
119 fields
= ('candidat', 'nom', 'path', )
122 class ProxyEvaluateur(Evaluateur
.candidats
.through
):
124 Ce proxy sert uniquement dans l'admin à disposer d'un libellé
129 verbose_name
= "évaluateur"
131 class EvaluateurInline(admin
.TabularInline
):
132 model
= ProxyEvaluateur
133 fields
= ('evaluateur',)
136 class CandidatAdmin(VersionAdmin
):
137 date_hierarchy
= 'date_creation'
138 list_display
= ('nom', 'prenom', 'offre_emploi','statut',
139 'voir_offre_emploi', #'note_evaluateur',
140 'calculer_moyenne', 'afficher_candidat',)
141 list_filter
= ('offre_emploi', )
144 'fields': ('offre_emploi', )
146 ('Informations personnelles', {
147 'fields': ('prenom','nom','genre', 'nationalite', 'date_naissance',
148 'situation_famille', 'nombre_dependant',)
151 'fields': ('telephone', 'email', 'adresse', 'ville',
152 'etat_province', 'code_postal', 'pays', )
154 ('Informations professionnelles', {
155 'fields': ('niveau_diplome','employeur_actuel',
156 'poste_actuel', 'domaine_professionnel',)
158 ('Options avancées', {
159 'classes': ('collapse',),
160 'fields': ('actif', 'statut', )
168 actions
= ['affecter_candidats_evaluateur', 'envoyer_courriel_candidats']
169 # Affecter un évaluateurs à des candidats
170 def affecter_candidats_evaluateur(modeladmin
, obj
, candidats
):
171 selected
= obj
.POST
.getlist(admin
.ACTION_CHECKBOX_NAME
)
173 return HttpResponseRedirect(reverse('affecter_evaluateurs_candidats')+
174 "?ids=%s" % (",".join(selected
)))
175 affecter_candidats_evaluateur
.short_description
= u
'Affecter évaluateur'
177 # Envoyer un courriel à des candidats
178 def envoyer_courriel_candidats(modeladmin
, obj
, candidats
):
179 selected
= obj
.POST
.getlist(admin
.ACTION_CHECKBOX_NAME
)
181 return HttpResponseRedirect(reverse('envoyer_courriel_candidats')+
182 "?ids=%s" % (",".join(selected
)))
183 envoyer_courriel_candidats
.short_description
= u
'Envoyer courriel'
185 # Évaluer un candidat
186 # TODO: Revérifier, si c'est mieux de rediriger vers Évaluation candidat et ensuite
187 # permettre l'évaluation ou laisser le reverse(evaluer_candidat)
188 def evaluer_candidat(self
, obj
):
189 return "<a href='%s?candidat__id__exact=%s'>Évaluer le candidat </a>" % \
190 (reverse('admin:recrutement_candidatevaluation_changelist'),
192 evaluer_candidat
.allow_tags
= True
193 evaluer_candidat
.short_description
= 'Évaluation'
195 # Afficher un candidat
196 def afficher_candidat(self
, obj
):
197 return "<a href='%s'>Voir le candidat</a>" % (reverse('admin:recrutement_proxycandidat_change', args
=(obj
.id,)))
198 afficher_candidat
.allow_tags
= True
199 afficher_candidat
.short_description
= u
'Afficher les détails du candidat'
201 # Voir l'offre d'emploi
202 def voir_offre_emploi(self
, obj
):
203 return "<a href='%s'>Voir l'offre d'emploi</a>" % \
204 (reverse('admin:recrutement_proxyoffreemploi_change',
205 args
=(obj
.offre_emploi
.id,)))
206 voir_offre_emploi
.allow_tags
= True
207 voir_offre_emploi
.short_description
= "Afficher l'offre d'emploi"
209 # Calculer la moyenne des notes
210 def calculer_moyenne(self
, obj
):
211 evaluations
= CandidatEvaluation
.objects
.filter(candidat
=obj
)
212 offre_emploi
= obj
.offre_emploi
214 notes
= [evaluation
.note
for evaluation
in evaluations
.all() \
215 if evaluation
.note
is not None]
217 if len(notes
) > 0 and offre_emploi
.date_limite
<= datetime
.date
.today():
218 moyenne_votes
= float(sum(notes
)) / len(notes
)
220 moyenne_votes
= "Non disponible"
222 calculer_moyenne
.allow_tags
= True
223 calculer_moyenne
.short_description
= "Moyenne des notes"
225 def add_delete_permission(self
, request
, obj
=None) :
226 user_groupes
= request
.user
.groups
.all()
227 if grp_drh_recrutement
in user_groupes
or \
228 grp_administrateurs_recrutement
in user_groupes
:
232 def has_add_permission(self
, request
):
233 return self
.add_delete_permission(request
, request
)
235 def has_delete_permission(self
, request
, obj
=None):
236 return self
.add_delete_permission(request
, request
)
238 def has_change_permission(self
, request
, obj
=None):
239 user_groupes
= request
.user
.groups
.all()
240 if grp_drh_recrutement
in user_groupes
or \
241 grp_administrateurs_recrutement
in user_groupes
:
245 def queryset(self
, obj
):
247 Spécifie un queryset limité, autrement Django exécute un
248 select_related() sans paramètre, ce qui a pour effet de charger tous
249 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
250 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
251 génération d'une requête infinie.
254 qs
= self
.model
._default_manager
.get_query_set()
255 # Si user est superuser afficher tous les candidats
256 user_groupes
= obj
.user
.groups
.all()
257 if not grp_drh_recrutement
in user_groupes
:
258 # Si le user n'est ni un évaluateur ni un administrateur régional,
262 if grp_evaluateurs_recrutement
in user_groupes
:
264 user
= Evaluateur
.objects
.get(user
=obj
.user
)
265 except Evaluateur
.DoesNotExist
:
268 elif grp_administrateurs_recrutement in user_groupes:
270 user = AdministrateurRegional.objects.get(user=obj.user)
271 except AdministrateurRegional.DoesNotExist:
276 ids
= [c
.id for c
in user
.candidats
.all()]
277 return qs
.select_related('candidats').filter(id__in
=ids
)
278 return qs
.select_related('candidats')
280 class ProxyCandidatAdmin(CandidatAdmin
):
281 readonly_fields
= ('statut', 'offre_emploi', 'prenom', 'nom',
282 'genre', 'nationalite', 'date_naissance',
283 'situation_famille', 'nombre_dependant', 'telephone',
284 'email', 'adresse', 'ville', 'etat_province',
285 'code_postal', 'pays', 'niveau_diplome',
286 'employeur_actuel', 'poste_actuel',
287 'domaine_professionnel',)
290 'fields': ('offre_emploi', )
292 ('Informations personnelles', {
293 'fields': ('prenom','nom','genre', 'nationalite', 'date_naissance',
294 'situation_famille', 'nombre_dependant',)
297 'fields': ('telephone', 'email', 'adresse', 'ville',
298 'etat_province', 'code_postal', 'pays', )
300 ('Informations professionnelles', {
301 'fields': ('niveau_diplome','employeur_actuel',
302 'poste_actuel', 'domaine_professionnel',)
307 def has_add_permission(self
, request
):
310 def has_delete_permission(self
, request
, obj
=None):
313 def has_change_permission(self
, request
, obj
=None):
314 user_groupes
= request
.user
.groups
.all()
315 if grp_drh_recrutement
in user_groupes
or \
316 grp_administrateurs_recrutement
in user_groupes
or \
317 grp_evaluateurs_recrutement
in user_groupes
:
321 class CandidatPieceAdmin(admin
.ModelAdmin
):
322 list_display
= ('nom', 'candidat', )
324 def queryset(self
, request
):
326 Spécifie un queryset limité, autrement Django exécute un
327 select_related() sans paramètre, ce qui a pour effet de charger tous
328 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
329 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
330 génération d'une requête infinie.
331 Affiche la liste de candidats que si le user connecté
332 possède un Evaluateur
334 qs
= self
.model
._default_manager
.get_query_set()
335 return qs
.select_related('candidat')
337 class EvaluateurAdmin(VersionAdmin
):
339 (None, {'fields': ('user', )}),
340 #(None, {'fields': ('candidats',)}),
343 class AdministrateurRegionalAdmin(VersionAdmin
):
346 class CandidatEvaluationAdmin(VersionAdmin
):
347 list_display
= ('_offre_emploi', '_candidat', '_note', '_commentaire',
349 readonly_fields
= ('candidat', 'evaluateur')
351 ('Évaluation du candidat', {
352 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
356 def _note(self
, obj
):
358 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
359 un lien pour Évaluer le candidat.
360 Sinon afficher la note.
363 return "<a href='%s'>Évaluer le candidat </a>" % \
364 (reverse('admin:recrutement_candidatevaluation_change',
365 args
=(obj
.candidat
.id,)))
367 _note
.allow_tags
= True
368 _note
.short_description
= "Votre note"
369 _note
.admin_order_field
= 'note'
371 def _candidat(self
, obj
):
372 return "<a href='%s'>%s</a>" \
373 % (reverse('admin:recrutement_proxycandidat_change',
374 args
=(obj
.candidat
.id,)), obj
.candidat
)
375 _candidat
.allow_tags
= True
376 _candidat
.short_description
= 'Candidat'
378 def _commentaire(self
, obj
):
380 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
381 dans le champ commentaire, Aucun au lieu de (None)
382 Sinon afficher la note.
384 if obj
.commentaire
is None:
386 return obj
.commentaire
387 _commentaire
.allow_tags
= True
388 _commentaire
.short_description
= "Commentaire"
391 def _offre_emploi(self
, obj
):
392 return "<a href='%s'>%s</a>" % \
393 (reverse('admin:recrutement_proxyoffreemploi_change',
394 args
=(obj
.candidat
.offre_emploi
.id,)), obj
.candidat
.offre_emploi
)
395 _offre_emploi
.allow_tags
= True
396 _offre_emploi
.short_description
= "Voir offre d'emploi"
397 _offre_emploi
.admin_order_field
= 'offre_emploi'
399 def queryset(self
, request
):
401 Afficher uniquement les évaluations de l'évaluateur, sauf si
402 l'utilisateur est super admin.
404 qs
= self
.model
._default_manager
.get_query_set()
405 user_groupes
= request
.user
.groups
.all()
406 if grp_drh_recrutement
in user_groupes
:
407 return qs
.select_related('offre_emploi')
410 evaluateur
= Evaluateur
.objects
.get(user
=request
.user
)
411 except Evaluateur
.DoesNotExist
:
414 candidats_evaluations
= CandidatEvaluation
.objects
.\
415 filter(evaluateur
=evaluateur
)
416 candidats_evaluations_ids
= [ce
.id for ce
in \
417 candidats_evaluations
.all()]
418 return qs
.select_related('offre_emploi').\
419 filter(id__in
=candidats_evaluations_ids
)
421 class CourrielTemplateAdmin(VersionAdmin
):
424 admin
.site
.register(OffreEmploi
, OffreEmploiAdmin
)
425 admin
.site
.register(Candidat
, CandidatAdmin
)
426 admin
.site
.register(CandidatEvaluation
, CandidatEvaluationAdmin
)
427 #admin.site.register(CourrielTemplate, CourrielTemplateAdmin)
428 admin
.site
.register(Evaluateur
, EvaluateurAdmin
)
429 #admin.site.register(AdministrateurRegional, AdministrateurRegionalAdmin)
430 admin
.site
.register(ProxyOffreEmploi
, ProxyOffreEmploiAdmin
)
431 admin
.site
.register(ProxyCandidat
, ProxyCandidatAdmin
)