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_evaluateurs_recrutement
, \
15 grp_drh_recrutement
, grp_directeurs_bureau_recrutement
, \
16 grp_administrateurs_recrutement
17 from recrutement
.forms
import *
20 IMPLANTATIONS_CENTRALES
= [15, 19]
22 class ProxyEvaluateur(Evaluateur
.offres_emploi
.through
):
24 Ce proxy sert uniquement dans l'admin à disposer d'un libellé
29 verbose_name
= "évaluateur"
31 class EvaluateurInline(admin
.TabularInline
):
32 model
= ProxyEvaluateur
33 fields
= ('evaluateur',)
36 class OffreEmploiAdmin(VersionAdmin
):
37 date_hierarchy
= 'date_creation'
38 list_display
= ('nom', 'date_limite', 'region', 'statut',
39 'est_affiche', '_candidatsList', )
40 exclude
= ('actif', 'poste_nom', 'resume',)
41 list_filter
= ('statut',)
42 actions
= ['affecter_evaluateurs_offre_emploi', ]
43 form
= OffreEmploiForm
44 inlines
= [EvaluateurInline
, ]
46 ### Actions à afficher
47 def get_actions(self
, request
):
48 actions
= super(OffreEmploiAdmin
, self
).get_actions(request
)
49 del actions
['delete_selected']
52 ### Affecter un évaluateurs à des offres d'emploi
53 def affecter_evaluateurs_offre_emploi(modeladmin
, obj
, candidats
):
54 selected
= obj
.POST
.getlist(admin
.ACTION_CHECKBOX_NAME
)
56 return HttpResponseRedirect(reverse('affecter_evaluateurs_offre_emploi')+
57 "?ids=%s" % (",".join(selected
)))
58 affecter_evaluateurs_offre_emploi
.short_description
= u
'Affecter évaluateur(s)'
60 ### Afficher la liste des candidats pour l'offre d'emploi
61 def _candidatsList(self
, obj
):
62 return "<a href='%s?offre_emploi__id__exact=%s'>Voir les candidats \
63 </a>" % (reverse('admin:recrutement_candidat_changelist'), obj
.id)
64 _candidatsList
.allow_tags
= True
65 _candidatsList
.short_description
= "Afficher la liste des candidats"
68 def get_form(self
, request
, obj
=None, **kwargs
):
69 form
= super(OffreEmploiAdmin
, self
).get_form(request
, obj
, **kwargs
)
70 employe
= get_emp(request
.user
)
71 user_groupes
= request
.user
.groups
.all()
74 if form
.declared_fields
.has_key('region'):
75 region_field
= form
.declared_fields
['region']
77 region_field
= form
.base_fields
['region']
79 if grp_drh_recrutement
in user_groupes
:
80 region_field
.queryset
= Region
.objects
.all()
82 region_field
.queryset
= Region
.objects
.\
83 filter(id=employe
.implantation
.region
.id)
86 if form
.declared_fields
.has_key('poste'):
87 poste_field
= form
.declared_fields
['poste']
89 poste_field
= form
.base_fields
['poste']
91 if grp_drh_recrutement
in user_groupes
:
92 poste_field
.queryset
= rh
.Poste
.objects
.all()
94 poste_field
.queryset
= rh
.Poste
.objects
.\
95 filter(implantation__region
=employe
.implantation
.region
).\
96 exclude(implantation__in
=IMPLANTATIONS_CENTRALES
)
99 if form
.declared_fields
.has_key('bureau'):
100 bureau_field
= form
.declared_fields
['bureau']
102 bureau_field
= form
.base_fields
['bureau']
104 if grp_drh_recrutement
in user_groupes
:
105 bureau_field
.queryset
= Bureau
.objects
.all()
107 bureau_field
.queryset
= Bureau
.objects
.\
108 filter(region
=employe
.implantation
.region
)
112 def queryset(self
, request
):
113 qs
= self
.model
._default_manager
.get_query_set()
114 user_groupes
= request
.user
.groups
.all()
115 if grp_drh_recrutement
in user_groupes
:
116 return qs
.select_related('offre_emploi')
118 if grp_directeurs_bureau_recrutement
in user_groupes
or \
119 grp_administrateurs_recrutement
in user_groupes
:
120 employe
= get_emp(request
.user
)
121 return qs
.select_related('offre_emploi').\
122 filter(region
=employe
.implantation
.region
)
124 if grp_evaluateurs_recrutement
in user_groupes
:
126 user
= Evaluateur
.objects
.get(user
=request
.user
)
127 except Evaluateur
.DoesNotExist
:
130 ids
= [o
.id for o
in user
.offres_emploi
.all()]
131 return qs
.select_related('offre_emploi').filter(id__in
=ids
)
134 ### Permission add, delete, change
135 def has_add_permission(self
, request
):
136 user_groupes
= request
.user
.groups
.all()
137 if grp_drh_recrutement
in user_groupes
or \
138 grp_directeurs_bureau_recrutement
in user_groupes
or \
139 grp_administrateurs_recrutement
in user_groupes
:
143 def has_delete_permission(self
, request
, obj
=None):
144 user_groupes
= request
.user
.groups
.all()
145 if grp_drh_recrutement
in user_groupes
or \
146 grp_directeurs_bureau_recrutement
in user_groupes
or \
147 grp_administrateurs_recrutement
in user_groupes
:
151 def has_change_permission(self
, request
, obj
=None):
152 user_groupes
= request
.user
.groups
.all()
153 if grp_drh_recrutement
in user_groupes
or \
154 grp_directeurs_bureau_recrutement
in user_groupes
or \
155 grp_administrateurs_recrutement
in user_groupes
:
159 class ProxyOffreEmploiAdmin(OffreEmploiAdmin
):
160 list_display
= ('nom', 'date_limite', 'region', 'statut',
162 readonly_fields
= ('description', 'bureau', 'duree_affectation',
163 'renumeration', 'debut_affectation', 'lieu_affectation',
164 'nom', 'resume', 'date_limite', 'region')
169 ('Description générale', {
170 'fields': ('resume','description', 'date_limite', )
173 'fields': ('lieu_affectation', 'bureau', 'region', )
176 'fields': ('debut_affectation', 'duree_affectation',
182 ### Actions à afficher
183 def get_actions(self
, request
):
184 actions
= super(ProxyOffreEmploiAdmin
, self
).get_actions(request
)
185 del actions
['affecter_evaluateurs_offre_emploi']
188 ### Lieu de redirection après le change
189 def response_change(self
, request
, obj
):
190 response
= super(ProxyOffreEmploiAdmin
, self
).response_change(request
,
192 user_groupes
= request
.user
.groups
.all()
193 if grp_drh_recrutement
in user_groupes
or \
194 grp_directeurs_bureau_recrutement
in user_groupes
or \
195 grp_administrateurs_recrutement
in user_groupes
:
196 return HttpResponseRedirect(reverse\
197 ('admin:recrutement_offreemploi_changelist'))
198 return HttpResponseRedirect(reverse\
199 ('admin:recrutement_proxyoffreemploi_changelist'))
202 def get_form(self
, request
, obj
=None, **kwargs
):
203 form
= super(OffreEmploiAdmin
, self
).get_form(request
, obj
, **kwargs
)
206 ### Permissions add, delete, change
207 def has_add_permission(self
, request
):
210 def has_delete_permission(self
, request
, obj
=None):
213 def has_change_permission(self
, request
, obj
=None):
214 user_groupes
= request
.user
.groups
.all()
215 if grp_evaluateurs_recrutement
in user_groupes
or \
216 grp_drh_recrutement
in user_groupes
or \
217 grp_directeurs_bureau_recrutement
in user_groupes
or \
218 grp_administrateurs_recrutement
in user_groupes
:
222 class CandidatPieceInline(admin
.TabularInline
):
223 model
= CandidatPiece
224 fields
= ('candidat', 'nom', 'path',)
228 class CandidatEvaluationInlineFormSet(BaseInlineFormSet
):
230 Empêche la suppression d'une évaluation pour le CandidatEvaluationInline
232 def __init__(self
, *args
, **kwargs
):
233 super(CandidatEvaluationInlineFormSet
, self
).__init__(*args
, **kwargs
)
234 self
.can_delete
= False
236 class CandidatEvaluationInline(admin
.TabularInline
):
237 model
= CandidatEvaluation
238 fields
= ('evaluateur', 'note', 'commentaire')
241 formset
= CandidatEvaluationInlineFormSet
244 def get_readonly_fields(self
, request
, obj
=None):
246 Empêche la modification des évaluations
249 return self
.readonly_fields
+('evaluateur', 'note', 'commentaire')
250 return self
.readonly_fields
252 class CandidatAdmin(VersionAdmin
):
253 exclude
= ('actif', )
254 date_hierarchy
= 'date_creation'
255 list_display
= ('nom', 'prenom', 'offre_emploi','statut',
256 'voir_offre_emploi', 'calculer_moyenne',
257 'afficher_candidat',)
258 list_filter
= ('offre_emploi', )
262 'fields': ('offre_emploi', )
264 ('Informations personnelles', {
265 'fields': ('prenom','nom','genre', 'nationalite',
266 'situation_famille', 'nombre_dependant',)
269 'fields': ('telephone', 'email', 'adresse', 'ville',
270 'etat_province', 'code_postal', 'pays', )
272 ('Informations professionnelles', {
273 'fields': ('niveau_diplome','employeur_actuel',
274 'poste_actuel', 'domaine_professionnel',)
277 'fields': ('statut', )
282 CandidatEvaluationInline
,
285 actions
= ['envoyer_courriel_candidats']
287 ### Actions à afficher
288 def get_actions(self
, request
):
289 actions
= super(CandidatAdmin
, self
).get_actions(request
)
290 del actions
['delete_selected']
293 ### Envoyer un courriel à des candidats
294 def envoyer_courriel_candidats(modeladmin
, obj
, candidats
):
295 selected
= obj
.POST
.getlist(admin
.ACTION_CHECKBOX_NAME
)
297 return HttpResponseRedirect(reverse('selectionner_template')+
298 "?ids=%s" % (",".join(selected
)))
299 envoyer_courriel_candidats
.short_description
= u
'Envoyer courriel'
301 ### Évaluer un candidat
302 def evaluer_candidat(self
, obj
):
303 return "<a href='%s?candidat__id__exact=%s'>Évaluer le candidat</a>" % \
304 (reverse('admin:recrutement_candidatevaluation_changelist'),
306 evaluer_candidat
.allow_tags
= True
307 evaluer_candidat
.short_description
= 'Évaluation'
309 ### Afficher un candidat
310 def afficher_candidat(self
, obj
):
311 return "<a href='%s'>Voir le candidat</a>" % \
312 (reverse('admin:recrutement_proxycandidat_change', args
=(obj
.id,)))
313 afficher_candidat
.allow_tags
= True
314 afficher_candidat
.short_description
= u
'Détails du candidat'
316 ### Voir l'offre d'emploi
317 def voir_offre_emploi(self
, obj
):
318 return "<a href='%s'>Voir l'offre d'emploi</a>" % \
319 (reverse('admin:recrutement_proxyoffreemploi_change',
320 args
=(obj
.offre_emploi
.id,)))
321 voir_offre_emploi
.allow_tags
= True
322 voir_offre_emploi
.short_description
= "Afficher l'offre d'emploi"
324 ### Calculer la moyenne des notes
325 def calculer_moyenne(self
, obj
):
326 evaluations
= CandidatEvaluation
.objects
.filter(candidat
=obj
)
327 offre_emploi
= obj
.offre_emploi
329 notes
= [evaluation
.note
for evaluation
in evaluations
.all() \
330 if evaluation
.note
is not None]
333 moyenne_votes
= float(sum(notes
)) / len(notes
)
335 moyenne_votes
= "Non disponible"
337 calculer_moyenne
.allow_tags
= True
338 calculer_moyenne
.short_description
= "Moyenne des notes"
340 ### Permissions add, delete, change
341 def has_add_permission(self
, request
):
342 user_groupes
= request
.user
.groups
.all()
343 if grp_drh_recrutement
in user_groupes
or \
344 grp_directeurs_bureau_recrutement
in user_groupes
or \
345 grp_administrateurs_recrutement
in user_groupes
:
349 def has_delete_permission(self
, request
, obj
=None):
350 user_groupes
= request
.user
.groups
.all()
351 if grp_drh_recrutement
in user_groupes
or \
352 grp_directeurs_bureau_recrutement
in user_groupes
or \
353 grp_administrateurs_recrutement
in user_groupes
:
357 def has_change_permission(self
, request
, obj
=None):
358 user_groupes
= request
.user
.groups
.all()
359 if grp_drh_recrutement
in user_groupes
or \
360 grp_directeurs_bureau_recrutement
in user_groupes
or \
361 grp_administrateurs_recrutement
in user_groupes
:
366 def queryset(self
, request
):
368 Spécifie un queryset limité, autrement Django exécute un
369 select_related() sans paramètre, ce qui a pour effet de charger tous
370 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
371 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
372 génération d'une requête infinie.
375 qs
= self
.model
._default_manager
.get_query_set()
376 user_groupes
= request
.user
.groups
.all()
377 if grp_drh_recrutement
in user_groupes
:
378 return qs
.select_related('candidats')
380 if grp_directeurs_bureau_recrutement
in user_groupes
or \
381 grp_administrateurs_recrutement
in user_groupes
:
382 employe
= get_emp(request
.user
)
383 return qs
.select_related('candidats').\
384 filter(offre_emploi__region
=employe
.implantation
.region
)
386 if grp_evaluateurs_recrutement
in user_groupes
:
388 user
= Evaluateur
.objects
.get(user
=request
.user
)
389 except Evaluateur
.DoesNotExist
:
392 offres_emploi
= [o
for o
in user
.offres_emploi
.all()]
393 candidat_ids
= [o
.candidat
.id for o
in offres_emploi
]
394 return qs
.select_related('candidats').filter(id__in
=candidat_ids
)
397 class ProxyCandidatAdmin(CandidatAdmin
):
398 readonly_fields
= ('statut', 'offre_emploi', 'prenom', 'nom',
399 'genre', 'nationalite', 'situation_famille',
400 'nombre_dependant', 'telephone', 'email', 'adresse',
401 'ville', 'etat_province', 'code_postal', 'pays',
402 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
403 'domaine_professionnel', 'pieces_jointes',)
406 'fields': ('offre_emploi', )
408 ('Informations personnelles', {
409 'fields': ('prenom','nom','genre', 'nationalite',
410 'situation_famille', 'nombre_dependant',)
413 'fields': ('telephone', 'email', 'adresse', 'ville',
414 'etat_province', 'code_postal', 'pays', )
416 ('Informations professionnelles', {
417 'fields': ('niveau_diplome','employeur_actuel',
418 'poste_actuel', 'domaine_professionnel',)
423 ### Lieu de redirection après le change
424 def response_change(self
, request
, obj
):
425 response
= super(ProxyCandidatAdmin
, self
).response_change(request
, obj
)
426 user_groupes
= request
.user
.groups
.all()
427 if grp_drh_recrutement
in user_groupes
or \
428 grp_directeurs_bureau_recrutement
in user_groupes
or \
429 grp_administrateurs_recrutement
in user_groupes
:
430 return HttpResponseRedirect(reverse\
431 ('admin:recrutement_candidat_changelist'))
432 return HttpResponseRedirect(reverse\
433 ('admin:recrutement_proxycandidat_changelist'))
435 ### Permissions add, delete, change
436 def has_add_permission(self
, request
):
439 def has_delete_permission(self
, request
, obj
=None):
442 def has_change_permission(self
, request
, obj
=None):
443 user_groupes
= request
.user
.groups
.all()
444 if grp_drh_recrutement
in user_groupes
or \
445 grp_evaluateurs_recrutement
in user_groupes
or \
446 grp_directeurs_bureau_recrutement
in user_groupes
or \
447 grp_administrateurs_recrutement
in user_groupes
:
451 class CandidatPieceAdmin(admin
.ModelAdmin
):
452 list_display
= ('nom', 'candidat', )
455 def queryset(self
, request
):
457 Spécifie un queryset limité, autrement Django exécute un
458 select_related() sans paramètre, ce qui a pour effet de charger tous
459 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
460 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
461 génération d'une requête infinie.
462 Affiche la liste de candidats que si le user connecté
463 possède un Evaluateur
465 qs
= self
.model
._default_manager
.get_query_set()
466 return qs
.select_related('candidat')
468 class EvaluateurAdmin(VersionAdmin
):
473 ("Offres d'emploi à évaluer", {
474 'fields': ('offres_emploi',)
478 ### Actions à afficher
479 def get_actions(self
, request
):
480 actions
= super(EvaluateurAdmin
, self
).get_actions(request
)
481 del actions
['delete_selected']
484 ### Permissions add, delete, change
485 def has_add_permission(self
, request
):
486 user_groupes
= request
.user
.groups
.all()
487 if grp_drh_recrutement
in user_groupes
or \
488 grp_directeurs_bureau_recrutement
in user_groupes
or \
489 grp_administrateurs_recrutement
in user_groupes
:
493 def has_delete_permission(self
, request
, obj
=None):
494 user_groupes
= request
.user
.groups
.all()
495 if grp_drh_recrutement
in user_groupes
or \
496 grp_directeurs_bureau_recrutement
in user_groupes
or \
497 grp_administrateurs_recrutement
in user_groupes
:
501 def has_change_permission(self
, request
, obj
=None):
502 user_groupes
= request
.user
.groups
.all()
503 if grp_drh_recrutement
in user_groupes
or \
504 grp_directeurs_bureau_recrutement
in user_groupes
or \
505 grp_administrateurs_recrutement
in user_groupes
:
509 class CandidatEvaluationAdmin(VersionAdmin
):
510 list_display
= ('_candidat', '_offre_emploi', 'evaluateur', '_note',
512 readonly_fields
= ('candidat', 'evaluateur')
514 ('Évaluation du candidat', {
515 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
519 ### Actions à afficher
520 def get_actions(self
, request
):
521 actions
= super(CandidatEvaluationAdmin
, self
).get_actions(request
)
522 del actions
['delete_selected']
526 def _note(self
, obj
):
528 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
529 un lien pour Évaluer le candidat.
530 Sinon afficher la note.
533 return "<a href='%s'>Candidat non évalué</a>" % \
534 (reverse('admin:recrutement_candidatevaluation_change',
536 return "<a href='%s'>%s</a>" % \
537 (reverse('admin:recrutement_candidatevaluation_change',
538 args
=(candidat_evaluation
.id,)), obj
.note
)
539 _note
.allow_tags
= True
540 _note
.short_description
= "Note"
541 _note
.admin_order_field
= 'note'
543 ### Lien en lecture seule vers le candidat
544 def _candidat(self
, obj
):
545 return "<a href='%s'>%s</a>" \
546 % (reverse('admin:recrutement_proxycandidat_change',
547 args
=(obj
.candidat
.id,)), obj
.candidat
)
548 _candidat
.allow_tags
= True
549 _candidat
.short_description
= 'Candidat'
551 ### Afficher commentaire
552 def _commentaire(self
, obj
):
554 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
555 dans le champ commentaire, Aucun au lieu de (None)
556 Sinon afficher la note.
558 if obj
.commentaire
is None:
560 return obj
.commentaire
561 _commentaire
.allow_tags
= True
562 _commentaire
.short_description
= "Commentaire"
564 ### Afficher offre d'emploi
565 def _offre_emploi(self
, obj
):
566 return "<a href='%s'>%s</a>" % \
567 (reverse('admin:recrutement_proxyoffreemploi_change',
568 args
=(obj
.candidat
.offre_emploi
.id,)), obj
.candidat
.offre_emploi
)
569 _offre_emploi
.allow_tags
= True
570 _offre_emploi
.short_description
= "Voir offre d'emploi"
572 ### Permissions add, delete, change
573 def has_add_permission(self
, request
):
574 user_groupes
= request
.user
.groups
.all()
575 if grp_drh_recrutement
in user_groupes
or \
576 grp_directeurs_bureau_recrutement
in user_groupes
or \
577 grp_administrateurs_recrutement
in user_groupes
:
581 def has_change_permission(self
, request
, obj
=None):
583 Permettre la visualisation dans la changelist
584 mais interdire l'accès à modifier l'objet si l'évaluateur n'est pas
587 return obj
is None or request
.user
== obj
.evaluateur
.user
590 def queryset(self
, request
):
592 Afficher uniquement les évaluations de l'évaluateur, sauf si
593 l'utilisateur est dans les groupes suivants.
595 qs
= self
.model
._default_manager
.get_query_set()
596 user_groupes
= request
.user
.groups
.all()
597 if grp_drh_recrutement
in user_groupes
or \
598 grp_directeurs_bureau_recrutement
in user_groupes
or \
599 grp_administrateurs_recrutement
in user_groupes
:
600 return qs
.select_related('offre_emploi')
603 evaluateur
= Evaluateur
.objects
.get(user
=request
.user
)
604 except Evaluateur
.DoesNotExist
:
607 candidats_evaluations
= CandidatEvaluation
.objects
.\
608 filter(evaluateur
=evaluateur
)
609 candidats_evaluations_ids
= [ce
.id for ce
in \
610 candidats_evaluations
.all()]
611 return qs
.select_related('offre_emploi').\
612 filter(id__in
=candidats_evaluations_ids
)
614 class CourrielTemplateAdmin(VersionAdmin
):
615 ### Actions à afficher
616 def get_actions(self
, request
):
617 actions
= super(CourrielTemplateAdmin
, self
).get_actions(request
)
618 del actions
['delete_selected']
621 admin
.site
.register(OffreEmploi
, OffreEmploiAdmin
)
622 admin
.site
.register(ProxyOffreEmploi
, ProxyOffreEmploiAdmin
)
623 admin
.site
.register(Candidat
, CandidatAdmin
)
624 admin
.site
.register(ProxyCandidat
, ProxyCandidatAdmin
)
625 admin
.site
.register(CandidatEvaluation
, CandidatEvaluationAdmin
)
626 admin
.site
.register(Evaluateur
, EvaluateurAdmin
)
627 admin
.site
.register(CourrielTemplate
, CourrielTemplateAdmin
)