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()
75 if form
.declared_fields
.has_key('region'):
76 region_field
= form
.declared_fields
['region']
78 region_field
= form
.base_fields
['region']
80 if grp_drh_recrutement
in user_groupes
:
81 region_field
.queryset
= Region
.objects
.all()
83 region_field
.queryset
= Region
.objects
.\
84 filter(id=employe
.implantation
.region
.id)
87 if form
.declared_fields
.has_key('poste'):
88 poste_field
= form
.declared_fields
['poste']
90 poste_field
= form
.base_fields
['poste']
92 if grp_drh_recrutement
in user_groupes
:
93 poste_field
.queryset
= rh
.Poste
.objects
.all()
95 poste_field
.queryset
= rh
.Poste
.objects
.\
96 filter(implantation__region
=employe
.implantation
.region
).\
97 exclude(implantation__in
=IMPLANTATIONS_CENTRALES
)
100 if form
.declared_fields
.has_key('bureau'):
101 bureau_field
= form
.declared_fields
['bureau']
103 bureau_field
= form
.base_fields
['bureau']
105 if grp_drh_recrutement
in user_groupes
:
106 bureau_field
.queryset
= Bureau
.objects
.all()
108 bureau_field
.queryset
= Bureau
.objects
.\
109 filter(region
=employe
.implantation
.region
)
114 def queryset(self
, request
):
115 qs
= self
.model
._default_manager
.get_query_set()
116 user_groupes
= request
.user
.groups
.all()
117 if grp_drh_recrutement
in user_groupes
:
118 return qs
.select_related('offre_emploi')
120 if grp_directeurs_bureau_recrutement
in user_groupes
or \
121 grp_administrateurs_recrutement
in user_groupes
:
122 employe
= get_emp(request
.user
)
123 return qs
.select_related('offre_emploi').\
124 filter(region
=employe
.implantation
.region
)
126 if grp_evaluateurs_recrutement
in user_groupes
:
128 user
= Evaluateur
.objects
.get(user
=request
.user
)
129 except Evaluateur
.DoesNotExist
:
132 ids
= [o
.id for o
in user
.offres_emploi
.all()]
133 return qs
.select_related('offre_emploi').filter(id__in
=ids
)
136 ### Permission add, delete, change
137 def has_add_permission(self
, request
):
138 user_groupes
= request
.user
.groups
.all()
139 if grp_drh_recrutement
in user_groupes
or \
140 grp_directeurs_bureau_recrutement
in user_groupes
or \
141 grp_administrateurs_recrutement
in user_groupes
:
145 def has_delete_permission(self
, request
, obj
=None):
146 user_groupes
= request
.user
.groups
.all()
147 if grp_drh_recrutement
in user_groupes
or \
148 grp_directeurs_bureau_recrutement
in user_groupes
or \
149 grp_administrateurs_recrutement
in user_groupes
:
153 def has_change_permission(self
, request
, obj
=None):
154 user_groupes
= request
.user
.groups
.all()
155 if grp_drh_recrutement
in user_groupes
or \
156 grp_directeurs_bureau_recrutement
in user_groupes
or \
157 grp_administrateurs_recrutement
in user_groupes
:
161 class ProxyOffreEmploiAdmin(OffreEmploiAdmin
):
162 list_display
= ('nom', 'date_limite', 'region', 'statut',
164 readonly_fields
= ('description', 'bureau', 'duree_affectation',
165 'renumeration', 'debut_affectation', 'lieu_affectation',
166 'nom', 'resume', 'date_limite', 'region', 'poste')
171 ('Description générale', {
172 'fields': ('description', 'date_limite', )
175 'fields': ('lieu_affectation', 'bureau', 'region', 'poste',)
178 'fields': ('debut_affectation', 'duree_affectation',
184 ### Actions à afficher
185 def get_actions(self
, request
):
186 actions
= super(ProxyOffreEmploiAdmin
, self
).get_actions(request
)
187 del actions
['affecter_evaluateurs_offre_emploi']
190 ### Lieu de redirection après le change
191 def response_change(self
, request
, obj
):
192 response
= super(ProxyOffreEmploiAdmin
,self
).response_change(request
,obj
)
193 return HttpResponseRedirect(reverse\
194 ('admin:recrutement_proxyoffreemploi_changelist'))
197 def get_form(self
, request
, obj
=None, **kwargs
):
198 form
= super(OffreEmploiAdmin
, self
).get_form(request
, obj
, **kwargs
)
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 ProxyCandidatAdmin(CandidatAdmin
):
394 readonly_fields
= ('statut', 'offre_emploi', 'prenom', 'nom',
395 'genre', 'nationalite', 'situation_famille',
396 'nombre_dependant', 'telephone', 'email', 'adresse',
397 'ville', 'etat_province', 'code_postal', 'pays',
398 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
399 'domaine_professionnel', 'pieces_jointes',)
402 'fields': ('offre_emploi', )
404 ('Informations personnelles', {
405 'fields': ('prenom','nom','genre', 'nationalite',
406 'situation_famille', 'nombre_dependant',)
409 'fields': ('telephone', 'email', 'adresse', 'ville',
410 'etat_province', 'code_postal', 'pays', )
412 ('Informations professionnelles', {
413 'fields': ('niveau_diplome','employeur_actuel',
414 'poste_actuel', 'domaine_professionnel',)
419 ### Lieu de redirection après le change
420 def response_change(self
, request
, obj
):
421 response
= super(ProxyCandidatAdmin
, self
).response_change(request
, obj
)
422 user_groupes
= request
.user
.groups
.all()
423 return HttpResponseRedirect(reverse\
424 ('admin:recrutement_proxycandidat_changelist'))
426 ### Permissions add, delete, change
427 def has_add_permission(self
, request
):
430 def has_delete_permission(self
, request
, obj
=None):
433 def has_change_permission(self
, request
, obj
=None):
434 user_groupes
= request
.user
.groups
.all()
435 if grp_drh_recrutement
in user_groupes
or \
436 grp_evaluateurs_recrutement
in user_groupes
or \
437 grp_directeurs_bureau_recrutement
in user_groupes
or \
438 grp_administrateurs_recrutement
in user_groupes
:
442 class CandidatPieceAdmin(admin
.ModelAdmin
):
443 list_display
= ('nom', 'candidat', )
446 def queryset(self
, request
):
448 Spécifie un queryset limité, autrement Django exécute un
449 select_related() sans paramètre, ce qui a pour effet de charger tous
450 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
451 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
452 génération d'une requête infinie.
453 Affiche la liste de candidats que si le user connecté
454 possède un Evaluateur
456 qs
= self
.model
._default_manager
.get_query_set()
457 return qs
.select_related('candidat')
459 class EvaluateurAdmin(VersionAdmin
):
464 ("Offres d'emploi à évaluer", {
465 'fields': ('offres_emploi',)
469 ### Actions à afficher
470 def get_actions(self
, request
):
471 actions
= super(EvaluateurAdmin
, self
).get_actions(request
)
472 del actions
['delete_selected']
475 ### Permissions add, delete, change
476 def has_add_permission(self
, request
):
477 user_groupes
= request
.user
.groups
.all()
478 if grp_drh_recrutement
in user_groupes
or \
479 grp_directeurs_bureau_recrutement
in user_groupes
or \
480 grp_administrateurs_recrutement
in user_groupes
:
484 def has_delete_permission(self
, request
, obj
=None):
485 user_groupes
= request
.user
.groups
.all()
486 if grp_drh_recrutement
in user_groupes
or \
487 grp_directeurs_bureau_recrutement
in user_groupes
or \
488 grp_administrateurs_recrutement
in user_groupes
:
492 def has_change_permission(self
, request
, obj
=None):
493 user_groupes
= request
.user
.groups
.all()
494 if grp_drh_recrutement
in user_groupes
or \
495 grp_directeurs_bureau_recrutement
in user_groupes
or \
496 grp_administrateurs_recrutement
in user_groupes
:
500 class CandidatEvaluationAdmin(VersionAdmin
):
501 list_display
= ('_candidat', '_offre_emploi', 'evaluateur', '_note',
503 readonly_fields
= ('candidat', 'evaluateur')
505 ('Évaluation du candidat', {
506 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
510 ### Actions à afficher
511 def get_actions(self
, request
):
512 actions
= super(CandidatEvaluationAdmin
, self
).get_actions(request
)
513 del actions
['delete_selected']
517 def _note(self
, obj
):
519 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
520 un lien pour Évaluer le candidat.
521 Sinon afficher la note.
524 return "<a href='%s'>Candidat non évalué</a>" % \
525 (reverse('admin:recrutement_candidatevaluation_change',
527 return "<a href='%s'>%s</a>" % \
528 (reverse('admin:recrutement_candidatevaluation_change',
529 args
=(candidat_evaluation
.id,)), obj
.note
)
530 _note
.allow_tags
= True
531 _note
.short_description
= "Note"
532 _note
.admin_order_field
= 'note'
534 ### Lien en lecture seule vers le candidat
535 def _candidat(self
, obj
):
536 return "<a href='%s'>%s</a>" \
537 % (reverse('admin:recrutement_proxycandidat_change',
538 args
=(obj
.candidat
.id,)), obj
.candidat
)
539 _candidat
.allow_tags
= True
540 _candidat
.short_description
= 'Candidat'
542 ### Afficher commentaire
543 def _commentaire(self
, obj
):
545 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
546 dans le champ commentaire, Aucun au lieu de (None)
547 Sinon afficher la note.
549 if obj
.commentaire
is None:
551 return obj
.commentaire
552 _commentaire
.allow_tags
= True
553 _commentaire
.short_description
= "Commentaire"
555 ### Afficher offre d'emploi
556 def _offre_emploi(self
, obj
):
557 return "<a href='%s'>%s</a>" % \
558 (reverse('admin:recrutement_proxyoffreemploi_change',
559 args
=(obj
.candidat
.offre_emploi
.id,)), obj
.candidat
.offre_emploi
)
560 _offre_emploi
.allow_tags
= True
561 _offre_emploi
.short_description
= "Voir offre d'emploi"
563 ### Permissions add, delete, change
564 def has_add_permission(self
, request
):
565 user_groupes
= request
.user
.groups
.all()
566 if grp_drh_recrutement
in user_groupes
or \
567 grp_directeurs_bureau_recrutement
in user_groupes
or \
568 grp_administrateurs_recrutement
in user_groupes
:
572 def has_change_permission(self
, request
, obj
=None):
574 Permettre la visualisation dans la changelist
575 mais interdire l'accès à modifier l'objet si l'évaluateur n'est pas
578 return obj
is None or request
.user
== obj
.evaluateur
.user
581 def queryset(self
, request
):
583 Afficher uniquement les évaluations de l'évaluateur, sauf si
584 l'utilisateur est dans les groupes suivants.
586 qs
= self
.model
._default_manager
.get_query_set()
587 user_groupes
= request
.user
.groups
.all()
588 if grp_drh_recrutement
in user_groupes
or \
589 grp_directeurs_bureau_recrutement
in user_groupes
or \
590 grp_administrateurs_recrutement
in user_groupes
:
591 return qs
.select_related('offre_emploi')
594 evaluateur
= Evaluateur
.objects
.get(user
=request
.user
)
595 except Evaluateur
.DoesNotExist
:
598 candidats_evaluations
= CandidatEvaluation
.objects
.\
599 filter(evaluateur
=evaluateur
)
600 candidats_evaluations_ids
= [ce
.id for ce
in \
601 candidats_evaluations
.all()]
602 return qs
.select_related('offre_emploi').\
603 filter(id__in
=candidats_evaluations_ids
)
605 class CourrielTemplateAdmin(VersionAdmin
):
606 ### Actions à afficher
607 def get_actions(self
, request
):
608 actions
= super(CourrielTemplateAdmin
, self
).get_actions(request
)
609 del actions
['delete_selected']
612 admin
.site
.register(OffreEmploi
, OffreEmploiAdmin
)
613 admin
.site
.register(ProxyOffreEmploi
, ProxyOffreEmploiAdmin
)
614 admin
.site
.register(Candidat
, CandidatAdmin
)
615 admin
.site
.register(ProxyCandidat
, ProxyCandidatAdmin
)
616 admin
.site
.register(CandidatEvaluation
, CandidatEvaluationAdmin
)
617 admin
.site
.register(Evaluateur
, EvaluateurAdmin
)
618 admin
.site
.register(CourrielTemplate
, CourrielTemplateAdmin
)