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'))
201 ### Permissions add, delete, change
202 def has_add_permission(self
, request
):
205 def has_delete_permission(self
, request
, obj
=None):
208 def has_change_permission(self
, request
, obj
=None):
209 user_groupes
= request
.user
.groups
.all()
210 if grp_evaluateurs_recrutement
in user_groupes
or \
211 grp_drh_recrutement
in user_groupes
or \
212 grp_directeurs_bureau_recrutement
in user_groupes
or \
213 grp_administrateurs_recrutement
in user_groupes
:
217 class CandidatPieceInline(admin
.TabularInline
):
218 model
= CandidatPiece
219 fields
= ('candidat', 'nom', 'path',)
223 class CandidatEvaluationInlineFormSet(BaseInlineFormSet
):
225 Empêche la suppression d'une évaluation pour le CandidatEvaluationInline
227 def __init__(self
, *args
, **kwargs
):
228 super(CandidatEvaluationInlineFormSet
, self
).__init__(*args
, **kwargs
)
229 self
.can_delete
= False
231 class CandidatEvaluationInline(admin
.TabularInline
):
232 model
= CandidatEvaluation
233 fields
= ('evaluateur', 'note', 'commentaire')
236 formset
= CandidatEvaluationInlineFormSet
239 def get_readonly_fields(self
, request
, obj
=None):
241 Empêche la modification des évaluations
244 return self
.readonly_fields
+('evaluateur', 'note', 'commentaire')
245 return self
.readonly_fields
247 class CandidatAdmin(VersionAdmin
):
248 exclude
= ('actif', )
249 date_hierarchy
= 'date_creation'
250 list_display
= ('nom', 'prenom', 'offre_emploi','statut',
251 'voir_offre_emploi', 'calculer_moyenne',
252 'afficher_candidat',)
253 list_filter
= ('offre_emploi', )
257 'fields': ('offre_emploi', )
259 ('Informations personnelles', {
260 'fields': ('prenom','nom','genre', 'nationalite',
261 'situation_famille', 'nombre_dependant',)
264 'fields': ('telephone', 'email', 'adresse', 'ville',
265 'etat_province', 'code_postal', 'pays', )
267 ('Informations professionnelles', {
268 'fields': ('niveau_diplome','employeur_actuel',
269 'poste_actuel', 'domaine_professionnel',)
272 'fields': ('statut', )
277 CandidatEvaluationInline
,
280 actions
= ['envoyer_courriel_candidats']
282 ### Actions à afficher
283 def get_actions(self
, request
):
284 actions
= super(CandidatAdmin
, self
).get_actions(request
)
285 del actions
['delete_selected']
288 ### Envoyer un courriel à des candidats
289 def envoyer_courriel_candidats(modeladmin
, obj
, candidats
):
290 selected
= obj
.POST
.getlist(admin
.ACTION_CHECKBOX_NAME
)
292 return HttpResponseRedirect(reverse('selectionner_template')+
293 "?ids=%s" % (",".join(selected
)))
294 envoyer_courriel_candidats
.short_description
= u
'Envoyer courriel'
296 ### Évaluer un candidat
297 def evaluer_candidat(self
, obj
):
298 return "<a href='%s?candidat__id__exact=%s'>Évaluer le candidat</a>" % \
299 (reverse('admin:recrutement_candidatevaluation_changelist'),
301 evaluer_candidat
.allow_tags
= True
302 evaluer_candidat
.short_description
= 'Évaluation'
304 ### Afficher un candidat
305 def afficher_candidat(self
, obj
):
306 return "<a href='%s'>Voir le candidat</a>" % \
307 (reverse('admin:recrutement_proxycandidat_change', args
=(obj
.id,)))
308 afficher_candidat
.allow_tags
= True
309 afficher_candidat
.short_description
= u
'Détails du candidat'
311 ### Voir l'offre d'emploi
312 def voir_offre_emploi(self
, obj
):
313 return "<a href='%s'>Voir l'offre d'emploi</a>" % \
314 (reverse('admin:recrutement_proxyoffreemploi_change',
315 args
=(obj
.offre_emploi
.id,)))
316 voir_offre_emploi
.allow_tags
= True
317 voir_offre_emploi
.short_description
= "Afficher l'offre d'emploi"
319 ### Calculer la moyenne des notes
320 def calculer_moyenne(self
, obj
):
321 evaluations
= CandidatEvaluation
.objects
.filter(candidat
=obj
)
322 offre_emploi
= obj
.offre_emploi
324 notes
= [evaluation
.note
for evaluation
in evaluations
.all() \
325 if evaluation
.note
is not None]
328 moyenne_votes
= float(sum(notes
)) / len(notes
)
330 moyenne_votes
= "Non disponible"
332 calculer_moyenne
.allow_tags
= True
333 calculer_moyenne
.short_description
= "Moyenne des notes"
335 ### Permissions add, delete, change
336 def has_add_permission(self
, request
):
337 user_groupes
= request
.user
.groups
.all()
338 if 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 grp_drh_recrutement
in user_groupes
or \
347 grp_directeurs_bureau_recrutement
in user_groupes
or \
348 grp_administrateurs_recrutement
in user_groupes
:
352 def has_change_permission(self
, request
, obj
=None):
353 user_groupes
= request
.user
.groups
.all()
354 if grp_drh_recrutement
in user_groupes
or \
355 grp_directeurs_bureau_recrutement
in user_groupes
or \
356 grp_administrateurs_recrutement
in user_groupes
:
361 def queryset(self
, request
):
363 Spécifie un queryset limité, autrement Django exécute un
364 select_related() sans paramètre, ce qui a pour effet de charger tous
365 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
366 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
367 génération d'une requête infinie.
370 qs
= self
.model
._default_manager
.get_query_set()
371 user_groupes
= request
.user
.groups
.all()
372 if grp_drh_recrutement
in user_groupes
:
373 return qs
.select_related('candidats')
375 if grp_directeurs_bureau_recrutement
in user_groupes
or \
376 grp_administrateurs_recrutement
in user_groupes
:
377 employe
= get_emp(request
.user
)
378 return qs
.select_related('candidats').\
379 filter(offre_emploi__region
=employe
.implantation
.region
)
381 if grp_evaluateurs_recrutement
in user_groupes
:
383 user
= Evaluateur
.objects
.get(user
=request
.user
)
384 except Evaluateur
.DoesNotExist
:
387 offres_emploi
= [o
for o
in user
.offres_emploi
.all()]
388 candidat_ids
= [o
.candidat
.id for o
in offres_emploi
]
389 return qs
.select_related('candidats').filter(id__in
=candidat_ids
)
393 class CandidatPieceROInline(CandidatPieceInline
):
396 template
= "admin/recrutement/proxycandidat/piece_jointe.html"
398 class ProxyCandidatAdmin(CandidatAdmin
):
399 readonly_fields
= ('statut', 'offre_emploi', 'prenom', 'nom',
400 'genre', 'nationalite', 'situation_famille',
401 'nombre_dependant', 'telephone', 'email', 'adresse',
402 'ville', 'etat_province', 'code_postal', 'pays',
403 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
404 'domaine_professionnel', 'pieces_jointes',)
407 'fields': ('offre_emploi', )
409 ('Informations personnelles', {
410 'fields': ('prenom','nom','genre', 'nationalite',
411 'situation_famille', 'nombre_dependant',)
414 'fields': ('telephone', 'email', 'adresse', 'ville',
415 'etat_province', 'code_postal', 'pays', )
417 ('Informations professionnelles', {
418 'fields': ('niveau_diplome','employeur_actuel',
419 'poste_actuel', 'domaine_professionnel',)
423 CandidatPieceROInline
,
425 ### Lieu de redirection après le change
426 def response_change(self
, request
, obj
):
427 response
= super(ProxyCandidatAdmin
, self
).response_change(request
, obj
)
428 user_groupes
= request
.user
.groups
.all()
429 if grp_drh_recrutement
in user_groupes
or \
430 grp_directeurs_bureau_recrutement
in user_groupes
or \
431 grp_administrateurs_recrutement
in user_groupes
:
432 return HttpResponseRedirect(reverse\
433 ('admin:recrutement_candidat_changelist'))
434 return HttpResponseRedirect(reverse\
435 ('admin:recrutement_proxycandidat_changelist'))
437 ### Permissions add, delete, change
438 def has_add_permission(self
, request
):
441 def has_delete_permission(self
, request
, obj
=None):
444 def has_change_permission(self
, request
, obj
=None):
445 user_groupes
= request
.user
.groups
.all()
446 if grp_drh_recrutement
in user_groupes
or \
447 grp_evaluateurs_recrutement
in user_groupes
or \
448 grp_directeurs_bureau_recrutement
in user_groupes
or \
449 grp_administrateurs_recrutement
in user_groupes
:
453 class CandidatPieceAdmin(admin
.ModelAdmin
):
454 list_display
= ('nom', 'candidat', )
457 def queryset(self
, request
):
459 Spécifie un queryset limité, autrement Django exécute un
460 select_related() sans paramètre, ce qui a pour effet de charger tous
461 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
462 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
463 génération d'une requête infinie.
464 Affiche la liste de candidats que si le user connecté
465 possède un Evaluateur
467 qs
= self
.model
._default_manager
.get_query_set()
468 return qs
.select_related('candidat')
470 class EvaluateurAdmin(VersionAdmin
):
475 ("Offres d'emploi à évaluer", {
476 'fields': ('offres_emploi',)
480 ### Actions à afficher
481 def get_actions(self
, request
):
482 actions
= super(EvaluateurAdmin
, self
).get_actions(request
)
483 del actions
['delete_selected']
486 ### Permissions add, delete, change
487 def has_add_permission(self
, request
):
488 user_groupes
= request
.user
.groups
.all()
489 if grp_drh_recrutement
in user_groupes
or \
490 grp_directeurs_bureau_recrutement
in user_groupes
or \
491 grp_administrateurs_recrutement
in user_groupes
:
495 def has_delete_permission(self
, request
, obj
=None):
496 user_groupes
= request
.user
.groups
.all()
497 if grp_drh_recrutement
in user_groupes
or \
498 grp_directeurs_bureau_recrutement
in user_groupes
or \
499 grp_administrateurs_recrutement
in user_groupes
:
503 def has_change_permission(self
, request
, obj
=None):
504 user_groupes
= request
.user
.groups
.all()
505 if grp_drh_recrutement
in user_groupes
or \
506 grp_directeurs_bureau_recrutement
in user_groupes
or \
507 grp_administrateurs_recrutement
in user_groupes
:
511 class CandidatEvaluationAdmin(VersionAdmin
):
512 list_display
= ('_candidat', '_offre_emploi', 'evaluateur', '_note',
514 readonly_fields
= ('candidat', 'evaluateur')
516 ('Évaluation du candidat', {
517 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
521 ### Actions à afficher
522 def get_actions(self
, request
):
523 actions
= super(CandidatEvaluationAdmin
, self
).get_actions(request
)
524 del actions
['delete_selected']
528 def _note(self
, obj
):
530 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
531 un lien pour Évaluer le candidat.
532 Sinon afficher la note.
535 return "<a href='%s'>Candidat non évalué</a>" % \
536 (reverse('admin:recrutement_candidatevaluation_change',
538 return "<a href='%s'>%s</a>" % \
539 (reverse('admin:recrutement_candidatevaluation_change',
540 args
=(candidat_evaluation
.id,)), obj
.note
)
541 _note
.allow_tags
= True
542 _note
.short_description
= "Note"
543 _note
.admin_order_field
= 'note'
545 ### Lien en lecture seule vers le candidat
546 def _candidat(self
, obj
):
547 return "<a href='%s'>%s</a>" \
548 % (reverse('admin:recrutement_proxycandidat_change',
549 args
=(obj
.candidat
.id,)), obj
.candidat
)
550 _candidat
.allow_tags
= True
551 _candidat
.short_description
= 'Candidat'
553 ### Afficher commentaire
554 def _commentaire(self
, obj
):
556 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
557 dans le champ commentaire, Aucun au lieu de (None)
558 Sinon afficher la note.
560 if obj
.commentaire
is None:
562 return obj
.commentaire
563 _commentaire
.allow_tags
= True
564 _commentaire
.short_description
= "Commentaire"
566 ### Afficher offre d'emploi
567 def _offre_emploi(self
, obj
):
568 return "<a href='%s'>%s</a>" % \
569 (reverse('admin:recrutement_proxyoffreemploi_change',
570 args
=(obj
.candidat
.offre_emploi
.id,)), obj
.candidat
.offre_emploi
)
571 _offre_emploi
.allow_tags
= True
572 _offre_emploi
.short_description
= "Voir offre d'emploi"
574 ### Permissions add, delete, change
575 def has_add_permission(self
, request
):
576 user_groupes
= request
.user
.groups
.all()
577 if grp_drh_recrutement
in user_groupes
or \
578 grp_directeurs_bureau_recrutement
in user_groupes
or \
579 grp_administrateurs_recrutement
in user_groupes
:
583 def has_change_permission(self
, request
, obj
=None):
585 Permettre la visualisation dans la changelist
586 mais interdire l'accès à modifier l'objet si l'évaluateur n'est pas
589 return obj
is None or request
.user
== obj
.evaluateur
.user
592 def queryset(self
, request
):
594 Afficher uniquement les évaluations de l'évaluateur, sauf si
595 l'utilisateur est dans les groupes suivants.
597 qs
= self
.model
._default_manager
.get_query_set()
598 user_groupes
= request
.user
.groups
.all()
599 if grp_drh_recrutement
in user_groupes
or \
600 grp_directeurs_bureau_recrutement
in user_groupes
or \
601 grp_administrateurs_recrutement
in user_groupes
:
602 return qs
.select_related('offre_emploi')
605 evaluateur
= Evaluateur
.objects
.get(user
=request
.user
)
606 except Evaluateur
.DoesNotExist
:
609 candidats_evaluations
= CandidatEvaluation
.objects
.\
610 filter(evaluateur
=evaluateur
)
611 candidats_evaluations_ids
= [ce
.id for ce
in \
612 candidats_evaluations
.all()]
613 return qs
.select_related('offre_emploi').\
614 filter(id__in
=candidats_evaluations_ids
)
616 class CourrielTemplateAdmin(VersionAdmin
):
617 ### Actions à afficher
618 def get_actions(self
, request
):
619 actions
= super(CourrielTemplateAdmin
, self
).get_actions(request
)
620 del actions
['delete_selected']
623 admin
.site
.register(OffreEmploi
, OffreEmploiAdmin
)
624 admin
.site
.register(ProxyOffreEmploi
, ProxyOffreEmploiAdmin
)
625 admin
.site
.register(Candidat
, CandidatAdmin
)
626 admin
.site
.register(ProxyCandidat
, ProxyCandidatAdmin
)
627 admin
.site
.register(CandidatEvaluation
, CandidatEvaluationAdmin
)
628 admin
.site
.register(Evaluateur
, EvaluateurAdmin
)
629 admin
.site
.register(CourrielTemplate
, CourrielTemplateAdmin
)