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
7 from django
.core
.files
.storage
import default_storage
9 from reversion
.admin
import VersionAdmin
10 from datamaster_modeles
.models
import Employe
, Implantation
, Region
11 from django
.forms
.models
import BaseInlineFormSet
13 from recrutement
.models
import *
14 from recrutement
.workflow
import grp_administrateurs_recrutement
,\
15 grp_evaluateurs_recrutement
, grp_drh_recrutement
18 class MetaAdmin(VersionAdmin):
19 def get_actions(self, request):
23 class OffreEmploiAdmin(VersionAdmin
):
24 date_hierarchy
= 'date_creation'
25 list_display
= ('nom', 'resume', 'date_limite', 'region', 'statut',
26 'est_affiche', '_candidatsList')
27 list_filter
= ('statut', 'est_affiche', )
28 actions
= ['affecter_evaluateurs_offre_emploi', ]
30 def get_actions(self
, request
):
31 actions
= super(OffreEmploiAdmin
, self
).get_actions(request
)
32 del actions
['delete_selected']
34 # Affecter un évaluateurs à des offres d'emploi
35 def affecter_evaluateurs_offre_emploi(modeladmin
, obj
, candidats
):
36 selected
= obj
.POST
.getlist(admin
.ACTION_CHECKBOX_NAME
)
38 return HttpResponseRedirect(reverse('affecter_evaluateurs_offre_emploi')+
39 "?ids=%s" % (",".join(selected
)))
40 affecter_evaluateurs_offre_emploi
.short_description
= u
'Affecter évaluateur(s)'
42 # Afficher la liste des candidats pour l'offre d'emploi
43 def _candidatsList(self
, obj
):
44 return "<a href='%s?offre_emploi__id__exact=%s'>Voir les candidats \
45 </a>" % (reverse('admin:recrutement_candidat_changelist'), obj
.id)
46 _candidatsList
.allow_tags
= True
47 _candidatsList
.short_description
= "Afficher la liste des candidats"
50 def queryset(self
, request
):
51 qs
= self
.model
._default_manager
.get_query_set()
52 # Si user est superuser afficher toutes les offres d'emploi
53 user_groupes
= request
.user
.groups
.all()
54 if not grp_drh_recrutement
in user_groupes
and \
55 not request
.user
.is_superuser
:
57 Si le user n'est ni un évaluateur ni un administrateur régional,
61 if grp_evaluateurs_recrutement
in user_groupes
:
63 user
= Evaluateur
.objects
.get(user
=request
.user
)
64 except Evaluateur
.DoesNotExist
:
66 elif grp_administrateurs_recrutement
in user_groupes
:
68 user
= AdministrateurRegional
.objects
.get(user
=request
.user
)
69 except AdministrateurRegional
.DoesNotExist
:
74 if type(user
) is AdministrateurRegional
:
75 region_ids
= [g
.id for g
in user
.regions
.all()]
76 return qs
.select_related('offre_emploi').\
77 filter(region__in
=region_ids
)
78 if type(user
) is Evaluateur
:
79 candidats
= [g
for g
in user
.candidats
.all()]
80 offre_emploi_ids
= [c
.offre_emploi
.id for c
in candidats
]
81 return qs
.select_related('offre_emploi').\
82 filter(id__in
=offre_emploi_ids
)
84 return qs
.select_related('offre_emploi')
86 def has_change_permission(self
, request
, obj
=None):
87 user_groupes
= request
.user
.groups
.all()
88 if grp_drh_recrutement
in user_groupes
or \
89 grp_administrateurs_recrutement
in user_groupes
or \
90 request
.user
.is_superuser
:
94 class ProxyOffreEmploiAdmin(OffreEmploiAdmin
):
95 list_display
= ('nom', 'resume', 'date_limite', 'region', 'statut',
97 readonly_fields
= ('description', 'bureau',
98 'duree_affectation', 'renumeration',
99 'debut_affectation', 'lieu_affectation', 'nom',
100 'resume', 'date_limite', 'region')
105 ('Description générale', {
106 'fields': ('resume','description', 'date_limite', )
109 'fields': ('lieu_affectation', 'bureau', 'region', )
112 'fields': ('debut_affectation', 'duree_affectation',
117 def response_change(self
, request
, obj
):
118 response
= super(ProxyOffreEmploiAdmin
, self
).response_change(request
, obj
)
119 user_groupes
= request
.user
.groups
.all()
120 if grp_drh_recrutement
in user_groupes
or \
121 request
.user
.is_superuser
:
122 return HttpResponseRedirect(reverse('admin:recrutement_offreemploi_changelist'))
123 return HttpResponseRedirect(reverse('admin:recrutement_proxyoffreemploi_changelist'))
125 def has_add_permission(self
, request
):
128 def has_delete_permission(self
, request
, obj
=None):
131 def has_change_permission(self
, request
, obj
=None):
132 user_groupes
= request
.user
.groups
.all()
133 if grp_evaluateurs_recrutement
in user_groupes
or \
134 grp_drh_recrutement
in user_groupes
or \
135 request
.user
.is_superuser
:
139 class ProxyEvaluateur(Evaluateur
.candidats
.through
):
141 Ce proxy sert uniquement dans l'admin à disposer d'un libellé
146 verbose_name
= "évaluateur"
148 class CandidatPieceInline(admin
.TabularInline
):
149 model
= CandidatPiece
150 fields
= ('candidat', 'nom', 'path',)
154 class EvaluateurInline(admin
.TabularInline
):
155 model
= ProxyEvaluateur
156 fields
= ('evaluateur',)
160 class CandidatEvaluationInlineFormSet(BaseInlineFormSet
):
162 Empêche la suppression d'une évaluation pour le CandidatEvaluationInline
164 def __init__(self
, *args
, **kwargs
):
165 super(CandidatEvaluationInlineFormSet
, self
).__init__(*args
, **kwargs
)
166 self
.can_delete
= False
168 class CandidatEvaluationInline(admin
.TabularInline
):
169 model
= CandidatEvaluation
170 fields
= ('evaluateur', 'note', 'commentaire')
173 formset
= CandidatEvaluationInlineFormSet
175 def get_readonly_fields(self
, request
, obj
=None):
177 Empêche la modification des évaluations
180 return self
.readonly_fields
+('evaluateur', 'note', 'commentaire')
181 return self
.readonly_fields
184 class CandidatAdmin(VersionAdmin
):
185 date_hierarchy
= 'date_creation'
186 list_display
= ('nom', 'prenom', 'offre_emploi','statut',
187 'voir_offre_emploi', 'calculer_moyenne',
188 'afficher_candidat',)
189 list_filter
= ('offre_emploi', )
192 'fields': ('offre_emploi', )
194 ('Informations personnelles', {
195 'fields': ('prenom','nom','genre', 'nationalite',
196 'situation_famille', 'nombre_dependant',)
199 'fields': ('telephone', 'email', 'adresse', 'ville',
200 'etat_province', 'code_postal', 'pays', )
202 ('Informations professionnelles', {
203 'fields': ('niveau_diplome','employeur_actuel',
204 'poste_actuel', 'domaine_professionnel',)
207 'fields': ('statut', )
213 CandidatEvaluationInline
,
216 actions
= ['affecter_candidats_evaluateur', 'envoyer_courriel_candidats']
218 def get_actions(self
, request
):
219 actions
= super(CandidatAdmin
, self
).get_actions(request
)
220 del actions
['delete_selected']
222 # Affecter un évaluateurs à des candidats
223 def affecter_candidats_evaluateur(modeladmin
, obj
, candidats
):
224 selected
= obj
.POST
.getlist(admin
.ACTION_CHECKBOX_NAME
)
226 return HttpResponseRedirect(reverse('affecter_evaluateurs_candidats')+
227 "?ids=%s" % (",".join(selected
)))
228 affecter_candidats_evaluateur
.short_description
= u
'Affecter évaluateur(s)'
230 # Envoyer un courriel à des candidats
231 def envoyer_courriel_candidats(modeladmin
, obj
, candidats
):
232 selected
= obj
.POST
.getlist(admin
.ACTION_CHECKBOX_NAME
)
234 return HttpResponseRedirect(reverse('selectionner_template')+
235 "?ids=%s" % (",".join(selected
)))
236 envoyer_courriel_candidats
.short_description
= u
'Envoyer courriel'
238 # Évaluer un candidat
239 def evaluer_candidat(self
, obj
):
240 return "<a href='%s?candidat__id__exact=%s'>Évaluer le candidat</a>" % \
241 (reverse('admin:recrutement_candidatevaluation_changelist'),
243 evaluer_candidat
.allow_tags
= True
244 evaluer_candidat
.short_description
= 'Évaluation'
246 # Afficher un candidat
247 def afficher_candidat(self
, obj
):
248 return "<a href='%s'>Voir le candidat</a>" % \
249 (reverse('admin:recrutement_proxycandidat_change', args
=(obj
.id,)))
250 afficher_candidat
.allow_tags
= True
251 afficher_candidat
.short_description
= u
'Détails du candidat'
253 # Voir l'offre d'emploi
254 def voir_offre_emploi(self
, obj
):
255 return "<a href='%s'>Voir l'offre d'emploi</a>" % \
256 (reverse('admin:recrutement_proxyoffreemploi_change',
257 args
=(obj
.offre_emploi
.id,)))
258 voir_offre_emploi
.allow_tags
= True
259 voir_offre_emploi
.short_description
= "Afficher l'offre d'emploi"
261 # Calculer la moyenne des notes
262 def calculer_moyenne(self
, obj
):
263 evaluations
= CandidatEvaluation
.objects
.filter(candidat
=obj
)
264 offre_emploi
= obj
.offre_emploi
266 notes
= [evaluation
.note
for evaluation
in evaluations
.all() \
267 if evaluation
.note
is not None]
269 if len(notes
) > 0 and offre_emploi
.date_limite
<= datetime
.date
.today():
270 moyenne_votes
= float(sum(notes
)) / len(notes
)
272 moyenne_votes
= "Non disponible"
274 calculer_moyenne
.allow_tags
= True
275 calculer_moyenne
.short_description
= "Moyenne des notes"
277 def add_delete_permission(self
, request
, obj
=None) :
278 user_groupes
= request
.user
.groups
.all()
279 if grp_drh_recrutement
in user_groupes
or \
280 grp_administrateurs_recrutement
in user_groupes
or \
281 request
.user
.is_superuser
:
285 def has_add_permission(self
, request
):
286 return self
.add_delete_permission(request
, request
)
288 def has_delete_permission(self
, request
, obj
=None):
289 return self
.add_delete_permission(request
, request
)
291 def has_change_permission(self
, request
, obj
=None):
292 user_groupes
= request
.user
.groups
.all()
293 if grp_drh_recrutement
in user_groupes
or \
294 grp_administrateurs_recrutement
in user_groupes
or \
295 request
.user
.is_superuser
:
299 def queryset(self
, request
):
301 Spécifie un queryset limité, autrement Django exécute un
302 select_related() sans paramètre, ce qui a pour effet de charger tous
303 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
304 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
305 génération d'une requête infinie.
308 qs
= self
.model
._default_manager
.get_query_set()
309 # Si user est superuser afficher tous les candidats
310 user_groupes
= request
.user
.groups
.all()
311 if not grp_drh_recrutement
in user_groupes
and \
312 not request
.user
.is_superuser
:
313 # Si le user n'est ni un évaluateur ni un administrateur régional,
317 if grp_evaluateurs_recrutement
in user_groupes
:
319 user
= Evaluateur
.objects
.get(user
=request
.user
)
320 except Evaluateur
.DoesNotExist
:
323 elif grp_administrateurs_recrutement in user_groupes:
325 user = AdministrateurRegional.objects.get(user=obj.user)
326 except AdministrateurRegional.DoesNotExist:
331 ids
= [c
.id for c
in user
.candidats
.all()]
332 return qs
.select_related('candidats').filter(id__in
=ids
)
333 return qs
.select_related('candidats')
335 class ProxyCandidatAdmin(CandidatAdmin
):
336 readonly_fields
= ('statut', 'offre_emploi', 'prenom', 'nom',
337 'genre', 'nationalite', 'situation_famille',
338 'nombre_dependant', 'telephone', 'email', 'adresse',
339 'ville', 'etat_province', 'code_postal', 'pays',
340 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
341 'domaine_professionnel', 'pieces_jointes',)
344 'fields': ('offre_emploi', )
346 ('Informations personnelles', {
347 'fields': ('prenom','nom','genre', 'nationalite',
348 'situation_famille', 'nombre_dependant',)
351 'fields': ('telephone', 'email', 'adresse', 'ville',
352 'etat_province', 'code_postal', 'pays', )
354 ('Informations professionnelles', {
355 'fields': ('niveau_diplome','employeur_actuel',
356 'poste_actuel', 'domaine_professionnel',)
361 def response_change(self
, request
, obj
):
362 response
= super(ProxyCandidatAdmin
, self
).response_change(request
, obj
)
363 user_groupes
= request
.user
.groups
.all()
364 if grp_drh_recrutement
in user_groupes
or \
365 request
.user
.is_superuser
:
366 return HttpResponseRedirect(reverse('admin:recrutement_candidat_changelist'))
367 return HttpResponseRedirect(reverse('admin:recrutement_proxycandidat_changelist'))
369 def has_add_permission(self
, request
):
372 def has_delete_permission(self
, request
, obj
=None):
375 def has_change_permission(self
, request
, obj
=None):
376 user_groupes
= request
.user
.groups
.all()
377 if grp_drh_recrutement
in user_groupes
or \
378 grp_administrateurs_recrutement
in user_groupes
or \
379 grp_evaluateurs_recrutement
in user_groupes
or \
380 request
.user
.is_superuser
:
384 class CandidatPieceAdmin(admin
.ModelAdmin
):
385 list_display
= ('nom', 'candidat', )
387 def queryset(self
, request
):
389 Spécifie un queryset limité, autrement Django exécute un
390 select_related() sans paramètre, ce qui a pour effet de charger tous
391 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
392 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
393 génération d'une requête infinie.
394 Affiche la liste de candidats que si le user connecté
395 possède un Evaluateur
397 qs
= self
.model
._default_manager
.get_query_set()
398 return qs
.select_related('candidat')
400 class EvaluateurAdmin(VersionAdmin
):
402 (None, {'fields': ('user', )}),
403 #(None, {'fields': ('candidats',)}),
406 def get_actions(self
, request
):
407 actions
= super(EvaluateurAdmin
, self
).get_actions(request
)
408 del actions
['delete_selected']
410 class AdministrateurRegionalAdmin(VersionAdmin
):
413 class CandidatEvaluationAdmin(VersionAdmin
):
414 list_display
= ('_candidat', '_offre_emploi', 'evaluateur', '_note',
416 readonly_fields
= ('candidat', 'evaluateur')
418 ('Évaluation du candidat', {
419 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
423 def get_actions(self
, request
):
424 actions
= super(CandidatEvaluationAdmin
, self
).get_actions(request
)
425 del actions
['delete_selected']
427 def _note(self
, obj
):
429 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
430 un lien pour Évaluer le candidat.
431 Sinon afficher la note.
433 evaluateur
= obj
.evaluateur
434 candidat
= obj
.candidat
435 candidat_evaluation
= CandidatEvaluation
.objects
.\
436 get(candidat
=candidat
, evaluateur
=evaluateur
)
438 return "<a href='%s'>Candidat non évalué</a>" % \
439 (reverse('admin:recrutement_candidatevaluation_change',
440 args
=(candidat_evaluation
.id,)))
441 return "<a href='%s'>%s</a>" % \
442 (reverse('admin:recrutement_candidatevaluation_change',
443 args
=(candidat_evaluation
.id,)), obj
.note
)
445 _note
.allow_tags
= True
446 _note
.short_description
= "Votre note"
447 _note
.admin_order_field
= 'note'
449 def _candidat(self
, obj
):
450 return "<a href='%s'>%s</a>" \
451 % (reverse('admin:recrutement_proxycandidat_change',
452 args
=(obj
.candidat
.id,)), obj
.candidat
)
453 _candidat
.allow_tags
= True
454 _candidat
.short_description
= 'Candidat'
456 def _commentaire(self
, obj
):
458 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
459 dans le champ commentaire, Aucun au lieu de (None)
460 Sinon afficher la note.
462 if obj
.commentaire
is None:
464 return obj
.commentaire
465 _commentaire
.allow_tags
= True
466 _commentaire
.short_description
= "Commentaire"
469 def _offre_emploi(self
, obj
):
470 return "<a href='%s'>%s</a>" % \
471 (reverse('admin:recrutement_proxyoffreemploi_change',
472 args
=(obj
.candidat
.offre_emploi
.id,)), obj
.candidat
.offre_emploi
)
473 _offre_emploi
.allow_tags
= True
474 _offre_emploi
.short_description
= "Voir offre d'emploi"
476 def has_change_permission(self
, request
, obj
=None):
478 Permettre la visualisation dans la changelist
479 mais interdire l'accès à modifier l'objet si l'évaluateur n'est pas
482 return obj
is None or request
.user
== obj
.evaluateur
.user
484 def queryset(self
, request
):
486 Afficher uniquement les évaluations de l'évaluateur, sauf si
487 l'utilisateur est super admin.
489 qs
= self
.model
._default_manager
.get_query_set()
490 user_groupes
= request
.user
.groups
.all()
491 if grp_drh_recrutement
in user_groupes
or \
492 request
.user
.is_superuser
:
493 return qs
.select_related('offre_emploi')
496 evaluateur
= Evaluateur
.objects
.get(user
=request
.user
)
497 except Evaluateur
.DoesNotExist
:
500 candidats_evaluations
= CandidatEvaluation
.objects
.\
501 filter(evaluateur
=evaluateur
)
502 candidats_evaluations_ids
= [ce
.id for ce
in \
503 candidats_evaluations
.all()]
504 return qs
.select_related('offre_emploi').\
505 filter(id__in
=candidats_evaluations_ids
)
507 class CourrielTemplateAdmin(VersionAdmin
):
508 def get_actions(self
, request
):
509 actions
= super(CourrielTemplateAdmin
, self
).get_actions(request
)
510 del actions
['delete_selected']
512 admin
.site
.register(OffreEmploi
, OffreEmploiAdmin
)
513 admin
.site
.register(ProxyOffreEmploi
, ProxyOffreEmploiAdmin
)
514 admin
.site
.register(Candidat
, CandidatAdmin
)
515 admin
.site
.register(ProxyCandidat
, ProxyCandidatAdmin
)
516 admin
.site
.register(CandidatEvaluation
, CandidatEvaluationAdmin
)
517 admin
.site
.register(Evaluateur
, EvaluateurAdmin
)
518 #admin.site.register(AdministrateurRegional, AdministrateurRegionalAdmin)
519 admin
.site
.register(CourrielTemplate
, CourrielTemplateAdmin
)