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
.forms
.models
import BaseInlineFormSet
8 from reversion
.admin
import VersionAdmin
9 from datamaster_modeles
.models
import Region
, Bureau
10 from project
.rh
import models
as rh
12 from project
.dae
.utils
import get_employe_from_user
as get_emp
13 from recrutement
.models
import *
14 from recrutement
.workflow
import grp_drh_recrutement
, grp_directeurs_bureau_recrutement
, \
15 grp_administrateurs_recrutement
, \
16 grp_correspondants_rh_recrutement
18 from recrutement
.forms
import *
21 IMPLANTATIONS_CENTRALES
= [15, 19]
23 class OffreEmploiAdmin(VersionAdmin
):
24 date_hierarchy
= 'date_creation'
25 list_display
= ('nom', 'date_limite', 'region', 'statut',
26 'est_affiche', '_candidatsList', )
27 exclude
= ('actif', 'poste_nom', 'resume',)
28 list_filter
= ('statut',)
29 actions
= ['affecter_evaluateurs_offre_emploi', ]
30 form
= OffreEmploiForm
32 ### Actions à afficher
33 def get_actions(self
, request
):
34 actions
= super(OffreEmploiAdmin
, self
).get_actions(request
)
35 del actions
['delete_selected']
38 ### Affecter un évaluateurs à des offres d'emploi
39 def affecter_evaluateurs_offre_emploi(modeladmin
, obj
, candidats
):
40 selected
= obj
.POST
.getlist(admin
.ACTION_CHECKBOX_NAME
)
42 return HttpResponseRedirect(reverse('affecter_evaluateurs_offre_emploi')+
43 "?ids=%s" % (",".join(selected
)))
44 affecter_evaluateurs_offre_emploi
.short_description
= u
'Affecter évaluateur(s)'
46 ### Afficher la liste des candidats pour l'offre d'emploi
47 def _candidatsList(self
, obj
):
48 return "<a href='%s?offre_emploi__id__exact=%s'>Voir les candidats \
49 </a>" % (reverse('admin:recrutement_candidat_changelist'), obj
.id)
50 _candidatsList
.allow_tags
= True
51 _candidatsList
.short_description
= "Afficher la liste des candidats"
54 def get_form(self
, request
, obj
=None, **kwargs
):
55 form
= super(OffreEmploiAdmin
, self
).get_form(request
, obj
, **kwargs
)
56 employe
= get_emp(request
.user
)
57 user_groupes
= request
.user
.groups
.all()
61 if form
.declared_fields
.has_key('region'):
62 region_field
= form
.declared_fields
['region']
64 region_field
= form
.base_fields
['region']
66 if grp_drh_recrutement
in user_groupes
:
67 region_field
.queryset
= Region
.objects
.all()
69 region_field
.queryset
= Region
.objects
.\
70 filter(id=employe
.implantation
.region
.id)
73 if form
.declared_fields
.has_key('poste'):
74 poste_field
= form
.declared_fields
['poste']
76 poste_field
= form
.base_fields
['poste']
78 if grp_drh_recrutement
in user_groupes
:
79 poste_field
.queryset
= rh
.Poste
.objects
.all()
81 poste_field
.queryset
= rh
.Poste
.objects
.\
82 filter(implantation__region
=employe
.implantation
.region
).\
83 exclude(implantation__in
=IMPLANTATIONS_CENTRALES
)
86 if form
.declared_fields
.has_key('bureau'):
87 bureau_field
= form
.declared_fields
['bureau']
89 bureau_field
= form
.base_fields
['bureau']
91 if grp_drh_recrutement
in user_groupes
:
92 bureau_field
.queryset
= Bureau
.objects
.all()
94 bureau_field
.queryset
= Bureau
.objects
.\
95 filter(region
=employe
.implantation
.region
)
100 def queryset(self
, request
):
101 qs
= self
.model
._default_manager
.get_query_set().select_related('offre_emploi')
102 user_groupes
= request
.user
.groups
.all()
103 if grp_drh_recrutement
in user_groupes
:
106 if grp_directeurs_bureau_recrutement
in user_groupes
or \
107 grp_correspondants_rh_recrutement
in user_groupes
or \
108 grp_administrateurs_recrutement
in user_groupes
:
109 employe
= get_emp(request
.user
)
110 return qs
.filter(region
=employe
.implantation
.region
)
112 if Evaluateur
.objects
.filter(user
=request
.user
).exists():
113 evaluateur
= Evaluateur
.objects
.get(user
=request
.user
)
114 offre_ids
= [e
.candidat
.offre_emploi_id
for e
in
115 CandidatEvaluation
.objects
.select_related('candidat').filter(evaluateur
=evaluateur
)]
116 return qs
.filter(id__in
=offre_ids
)
120 ### Permission add, delete, change
121 def has_add_permission(self
, request
):
122 user_groupes
= request
.user
.groups
.all()
123 if request
.user
.is_superuser
is True or \
124 grp_drh_recrutement
in user_groupes
or \
125 grp_directeurs_bureau_recrutement
in user_groupes
or \
126 grp_administrateurs_recrutement
in user_groupes
:
130 def has_delete_permission(self
, request
, obj
=None):
131 user_groupes
= request
.user
.groups
.all()
132 if request
.user
.is_superuser
is True or \
133 grp_drh_recrutement
in user_groupes
or \
134 grp_directeurs_bureau_recrutement
in user_groupes
or \
135 grp_administrateurs_recrutement
in user_groupes
:
139 def has_change_permission(self
, request
, obj
=None):
140 user_groupes
= request
.user
.groups
.all()
141 if request
.user
.is_superuser
is True or \
142 grp_drh_recrutement
in user_groupes
or \
143 grp_directeurs_bureau_recrutement
in user_groupes
or \
144 grp_administrateurs_recrutement
in user_groupes
:
148 class ProxyOffreEmploiAdmin(OffreEmploiAdmin
):
149 list_display
= ('nom', 'date_limite', 'region', 'statut',
151 readonly_fields
= ('description', 'bureau', 'duree_affectation',
152 'renumeration', 'debut_affectation', 'lieu_affectation',
153 'nom', 'resume', 'date_limite', 'region', 'poste')
158 ('Description générale', {
159 'fields': ('description', 'date_limite', )
162 'fields': ('lieu_affectation', 'bureau', 'region', 'poste',)
165 'fields': ('debut_affectation', 'duree_affectation',
172 ### Lieu de redirection après le change
173 def response_change(self
, request
, obj
):
174 return HttpResponseRedirect(reverse\
175 ('admin:recrutement_proxyoffreemploi_changelist'))
178 def get_form(self
, request
, obj
=None, **kwargs
):
179 form
= super(OffreEmploiAdmin
, self
).get_form(request
, obj
, **kwargs
)
182 ### Permissions add, delete, change
183 def has_add_permission(self
, request
):
186 def has_delete_permission(self
, request
, obj
=None):
189 def has_change_permission(self
, request
, obj
=None):
192 class CandidatPieceInline(admin
.TabularInline
):
193 model
= CandidatPiece
194 fields
= ('candidat', 'nom', 'path',)
198 class ReadOnlyCandidatPieceInline(CandidatPieceInline
):
199 readonly_fields
= ('candidat', 'nom', 'path', )
203 class CandidatEvaluationInlineFormSet(BaseInlineFormSet
):
205 Empêche la suppression d'une évaluation pour le CandidatEvaluationInline
207 def __init__(self
, *args
, **kwargs
):
208 super(CandidatEvaluationInlineFormSet
, self
).__init__(*args
, **kwargs
)
209 self
.can_delete
= False
211 class CandidatEvaluationInline(admin
.TabularInline
):
212 model
= CandidatEvaluation
213 fields
= ('evaluateur', 'note', 'commentaire')
216 formset
= CandidatEvaluationInlineFormSet
219 def get_readonly_fields(self
, request
, obj
=None):
221 Empêche la modification des évaluations
224 return self
.readonly_fields
+('evaluateur', 'note', 'commentaire')
225 return self
.readonly_fields
227 class CandidatAdmin(VersionAdmin
):
228 search_fields
= ('nom', 'prenom' )
229 exclude
= ('actif', )
230 list_editable
= ('statut', )
231 list_display
= ('nom', 'prenom', 'offre_emploi',
232 'voir_offre_emploi', 'calculer_moyenne',
233 'afficher_candidat', '_date_creation', 'statut', )
234 list_filter
= ('offre_emploi', 'offre_emploi__region', 'statut', )
238 'fields': ('offre_emploi', )
240 ('Informations personnelles', {
241 'fields': ('prenom','nom','genre', 'nationalite',
242 'situation_famille', 'nombre_dependant',)
245 'fields': ('telephone', 'email', 'adresse', 'ville',
246 'etat_province', 'code_postal', 'pays', )
248 ('Informations professionnelles', {
249 'fields': ('niveau_diplome','employeur_actuel',
250 'poste_actuel', 'domaine_professionnel',)
253 'fields': ('statut', )
258 CandidatEvaluationInline
,
261 actions
= ['envoyer_courriel_candidats']
263 def _date_creation(self
, obj
):
264 return obj
.date_creation
265 _date_creation
.order_field
= "date_creation"
266 _date_creation
.short_description
= "Date de création"
268 ### Actions à afficher
269 def get_actions(self
, request
):
270 actions
= super(CandidatAdmin
, self
).get_actions(request
)
271 del actions
['delete_selected']
274 ### Envoyer un courriel à des candidats
275 def envoyer_courriel_candidats(modeladmin
, obj
, candidats
):
276 selected
= obj
.POST
.getlist(admin
.ACTION_CHECKBOX_NAME
)
278 return HttpResponseRedirect(reverse('selectionner_template')+
279 "?ids=%s" % (",".join(selected
)))
280 envoyer_courriel_candidats
.short_description
= u
'Envoyer courriel'
282 ### Évaluer un candidat
283 def evaluer_candidat(self
, obj
):
284 return "<a href='%s?candidat__id__exact=%s'>Évaluer le candidat</a>" % \
285 (reverse('admin:recrutement_candidatevaluation_changelist'),
287 evaluer_candidat
.allow_tags
= True
288 evaluer_candidat
.short_description
= 'Évaluation'
290 ### Afficher un candidat
291 def afficher_candidat(self
, obj
):
292 return "<a href='%s'>Voir le candidat</a>" % \
293 (reverse('admin:recrutement_proxycandidat_change', args
=(obj
.id,)))
294 afficher_candidat
.allow_tags
= True
295 afficher_candidat
.short_description
= u
'Détails du candidat'
297 ### Voir l'offre d'emploi
298 def voir_offre_emploi(self
, obj
):
299 return "<a href='%s'>Voir l'offre d'emploi</a>" % \
300 (reverse('admin:recrutement_proxyoffreemploi_change',
301 args
=(obj
.offre_emploi
.id,)))
302 voir_offre_emploi
.allow_tags
= True
303 voir_offre_emploi
.short_description
= "Afficher l'offre d'emploi"
305 ### Calculer la moyenne des notes
306 def calculer_moyenne(self
, obj
):
307 evaluations
= CandidatEvaluation
.objects
.filter(candidat
=obj
)
309 notes
= [evaluation
.note
for evaluation
in evaluations \
310 if evaluation
.note
is not None]
313 moyenne_votes
= float(sum(notes
)) / len(notes
)
315 moyenne_votes
= "Non disponible"
317 totales
= len(evaluations
)
320 if obj
.statut
== 'REC':
321 if totales
== faites
:
323 elif faites
> 0 and float(totales
) / float(faites
) >= 2:
330 return """<span style="color: %s;">%s (%s/%s)</span>""" % (color
, moyenne_votes
, faites
, totales
)
331 calculer_moyenne
.allow_tags
= True
332 calculer_moyenne
.short_description
= "Notation"
334 ### Permissions add, delete, change
335 def has_add_permission(self
, request
):
336 user_groupes
= request
.user
.groups
.all()
337 if request
.user
.is_superuser
is True or \
338 grp_drh_recrutement
in user_groupes
or \
339 grp_directeurs_bureau_recrutement
in user_groupes
or \
340 grp_administrateurs_recrutement
in user_groupes
:
344 def has_delete_permission(self
, request
, obj
=None):
345 user_groupes
= request
.user
.groups
.all()
346 if request
.user
.is_superuser
is True or \
347 grp_drh_recrutement
in user_groupes
or \
348 grp_directeurs_bureau_recrutement
in user_groupes
or \
349 grp_administrateurs_recrutement
in user_groupes
:
353 def has_change_permission(self
, request
, obj
=None):
354 user_groupes
= request
.user
.groups
.all()
355 if request
.user
.is_superuser
is True or \
356 grp_correspondants_rh_recrutement
in user_groupes
or \
357 grp_drh_recrutement
in user_groupes
or \
358 grp_directeurs_bureau_recrutement
in user_groupes
or \
359 grp_administrateurs_recrutement
in user_groupes
:
364 def queryset(self
, request
):
366 Spécifie un queryset limité, autrement Django exécute un
367 select_related() sans paramètre, ce qui a pour effet de charger tous
368 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
369 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
370 génération d'une requête infinie.
373 qs
= self
.model
._default_manager
.get_query_set()
374 user_groupes
= request
.user
.groups
.all()
375 if grp_drh_recrutement
in user_groupes
:
376 return qs
.select_related('candidats')
378 if grp_directeurs_bureau_recrutement
in user_groupes
or \
379 grp_correspondants_rh_recrutement
in user_groupes
or \
380 grp_administrateurs_recrutement
in user_groupes
:
381 employe
= get_emp(request
.user
)
382 return qs
.select_related('candidats').\
383 filter(offre_emploi__region
=employe
.implantation
.region
)
385 if Evaluateur
.objects
.filter(user
=request
.user
).exists():
386 evaluateur
= Evaluateur
.objects
.get(user
=request
.user
)
387 candidat_ids
= [e
.candidat
.id for e
in
388 CandidatEvaluation
.objects
.filter(evaluateur
=evaluateur
)]
389 return qs
.select_related('candidats').filter(id__in
=candidat_ids
)
393 class ProxyCandidatAdmin(CandidatAdmin
):
395 readonly_fields
= ('statut', 'offre_emploi', 'prenom', 'nom',
396 'genre', 'nationalite', 'situation_famille',
397 'nombre_dependant', 'telephone', 'email', 'adresse',
398 'ville', 'etat_province', 'code_postal', 'pays',
399 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
400 'domaine_professionnel', 'pieces_jointes',)
403 'fields': ('offre_emploi', )
405 ('Informations personnelles', {
406 'fields': ('prenom','nom','genre', 'nationalite',
407 'situation_famille', 'nombre_dependant',)
410 'fields': ('telephone', 'email', 'adresse', 'ville',
411 'etat_province', 'code_postal', 'pays', )
413 ('Informations professionnelles', {
414 'fields': ('niveau_diplome','employeur_actuel',
415 'poste_actuel', 'domaine_professionnel',)
418 inlines
= (CandidatEvaluationInline
, )
420 def has_add_permission(self
, request
):
423 def has_delete_permission(self
, request
, obj
=None):
426 def has_change_permission(self
, request
, obj
=None):
429 class CandidatPieceAdmin(admin
.ModelAdmin
):
430 list_display
= ('nom', 'candidat', )
433 def queryset(self
, request
):
435 Spécifie un queryset limité, autrement Django exécute un
436 select_related() sans paramètre, ce qui a pour effet de charger tous
437 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
438 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
439 génération d'une requête infinie.
440 Affiche la liste de candidats que si le user connecté
441 possède un Evaluateur
443 qs
= self
.model
._default_manager
.get_query_set()
444 return qs
.select_related('candidat')
446 class EvaluateurAdmin(VersionAdmin
):
453 ### Actions à afficher
454 def get_actions(self
, request
):
455 actions
= super(EvaluateurAdmin
, self
).get_actions(request
)
456 del actions
['delete_selected']
459 ### Permissions add, delete, change
460 def has_add_permission(self
, request
):
461 user_groupes
= request
.user
.groups
.all()
462 if request
.user
.is_superuser
is True or \
463 grp_drh_recrutement
in user_groupes
or \
464 grp_directeurs_bureau_recrutement
in user_groupes
or \
465 grp_administrateurs_recrutement
in user_groupes
:
469 def has_delete_permission(self
, request
, obj
=None):
470 user_groupes
= request
.user
.groups
.all()
471 if request
.user
.is_superuser
is True or \
472 grp_drh_recrutement
in user_groupes
or \
473 grp_directeurs_bureau_recrutement
in user_groupes
or \
474 grp_administrateurs_recrutement
in user_groupes
:
478 def has_change_permission(self
, request
, obj
=None):
479 user_groupes
= request
.user
.groups
.all()
480 if request
.user
.is_superuser
is True or \
481 grp_drh_recrutement
in user_groupes
or \
482 grp_directeurs_bureau_recrutement
in user_groupes
or \
483 grp_administrateurs_recrutement
in user_groupes
:
487 class CandidatEvaluationAdmin(admin
.ModelAdmin
):
488 search_fields
= ('candidat__nom', 'candidat__prenom' )
489 list_display
= ('_candidat', '_statut', '_offre_emploi', 'evaluateur', '_note',
491 readonly_fields
= ('candidat', 'evaluateur')
492 list_filter
= ('candidat__statut', 'candidat__offre_emploi',)
494 ('Évaluation du candidat', {
495 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
499 ### Actions à afficher
500 def get_actions(self
, request
):
501 actions
= super(CandidatEvaluationAdmin
, self
).get_actions(request
)
502 del actions
['delete_selected']
506 def _note(self
, obj
):
508 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
509 un lien pour Évaluer le candidat.
510 Sinon afficher la note.
512 page
= self
.model
.__name__
.lower()
513 redirect_url
= 'admin:recrutement_%s_change' % page
515 return "<a href='%s'>Candidat non évalué</a>" % (reverse(redirect_url
, args
=(obj
.id,)))
516 return "<a href='%s'>%s</a>" % (reverse(redirect_url
, args
=(obj
.id,)), obj
.note
)
517 _note
.allow_tags
= True
518 _note
.short_description
= "Note"
519 _note
.admin_order_field
= 'note'
521 def _statut(self
, obj
):
522 return obj
.candidat
.get_statut_display()
523 _statut
.order_field
= 'candidat__statut'
524 _statut
.short_description
= 'Statut'
527 ### Lien en lecture seule vers le candidat
528 def _candidat(self
, obj
):
529 return "<a href='%s'>%s</a>" \
530 % (reverse('admin:recrutement_proxycandidat_change',
531 args
=(obj
.candidat
.id,)), obj
.candidat
)
532 _candidat
.allow_tags
= True
533 _candidat
.short_description
= 'Candidat'
535 ### Afficher commentaire
536 def _commentaire(self
, obj
):
538 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
539 dans le champ commentaire, Aucun au lieu de (None)
540 Sinon afficher la note.
542 if obj
.commentaire
is None:
544 return obj
.commentaire
545 _commentaire
.allow_tags
= True
546 _commentaire
.short_description
= "Commentaire"
548 ### Afficher offre d'emploi
549 def _offre_emploi(self
, obj
):
550 return "<a href='%s'>%s</a>" % \
551 (reverse('admin:recrutement_proxyoffreemploi_change',
552 args
=(obj
.candidat
.offre_emploi
.id,)), obj
.candidat
.offre_emploi
)
553 _offre_emploi
.allow_tags
= True
554 _offre_emploi
.short_description
= "Voir offre d'emploi"
556 def has_add_permission(self
, request
):
559 def has_delete_permission(self
, request
, obj
=None):
562 def has_change_permission(self
, request
, obj
=None):
564 Permettre la visualisation dans la changelist
565 mais interdire l'accès à modifier l'objet si l'évaluateur n'est pas
568 user_groupes
= request
.user
.groups
.all()
570 if request
.user
.is_superuser
or \
571 grp_drh_recrutement
in user_groupes
or \
572 grp_correspondants_rh_recrutement
in user_groupes
or \
573 grp_directeurs_bureau_recrutement
in user_groupes
or \
574 grp_administrateurs_recrutement
in user_groupes
:
580 Evaluateur
.objects
.get(user
=request
.user
)
583 is_evaluateur
= False
585 if obj
is None and (is_recrutement
or is_evaluateur
):
588 if request
.user
.is_superuser
is True:
592 return request
.user
== obj
.evaluateur
.user
597 def queryset(self
, request
):
599 Afficher uniquement les évaluations de l'évaluateur, sauf si
600 l'utilisateur est dans les groupes suivants.
602 qs
= self
.model
._default_manager
.get_query_set().select_related('offre_emploi')
603 user_groupes
= request
.user
.groups
.all()
605 if grp_drh_recrutement
in user_groupes
or \
606 grp_correspondants_rh_recrutement
in user_groupes
or \
607 grp_directeurs_bureau_recrutement
in user_groupes
or \
608 grp_administrateurs_recrutement
in user_groupes
:
611 evaluateur
= Evaluateur
.objects
.get(user
=request
.user
)
612 candidats_evaluations
= \
613 CandidatEvaluation
.objects
.filter(evaluateur
=evaluateur
,
614 candidat__statut__in
=('REC', ))
615 candidats_evaluations_ids
= [ce
.id for ce
in candidats_evaluations
]
616 return qs
.filter(id__in
=candidats_evaluations_ids
)
619 class MesCandidatEvaluationAdmin(CandidatEvaluationAdmin
):
621 def has_change_permission(self
, request
, obj
=None):
623 Evaluateur
.objects
.get(user
=request
.user
)
626 is_evaluateur
= False
628 if obj
is None or is_evaluateur
:
632 return request
.user
== obj
.evaluateur
.user
636 def queryset(self
, request
):
637 qs
= self
.model
._default_manager
.get_query_set().select_related('offre_emploi')
638 evaluateur
= Evaluateur
.objects
.get(user
=request
.user
)
639 candidats_evaluations
= \
640 CandidatEvaluation
.objects
.filter(evaluateur
=evaluateur
,
641 candidat__statut__in
=('REC', ))
642 candidats_evaluations_ids
= [ce
.id for ce
in candidats_evaluations
]
643 return qs
.filter(id__in
=candidats_evaluations_ids
)
646 class CourrielTemplateAdmin(VersionAdmin
):
647 ### Actions à afficher
648 def get_actions(self
, request
):
649 actions
= super(CourrielTemplateAdmin
, self
).get_actions(request
)
650 del actions
['delete_selected']
653 admin
.site
.register(OffreEmploi
, OffreEmploiAdmin
)
654 admin
.site
.register(ProxyOffreEmploi
, ProxyOffreEmploiAdmin
)
655 admin
.site
.register(Candidat
, CandidatAdmin
)
656 admin
.site
.register(ProxyCandidat
, ProxyCandidatAdmin
)
657 admin
.site
.register(CandidatEvaluation
, CandidatEvaluationAdmin
)
658 admin
.site
.register(MesCandidatEvaluation
, MesCandidatEvaluationAdmin
)
659 admin
.site
.register(Evaluateur
, EvaluateurAdmin
)
660 admin
.site
.register(CourrielTemplate
, CourrielTemplateAdmin
)