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 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 grp_evaluateurs_recrutement
in user_groupes
:
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):
190 user_groupes
= request
.user
.groups
.all()
191 if request
.user
.is_superuser
is True or \
192 grp_evaluateurs_recrutement
in user_groupes
or \
193 grp_drh_recrutement
in user_groupes
or \
194 grp_directeurs_bureau_recrutement
in user_groupes
or \
195 grp_administrateurs_recrutement
in user_groupes
:
199 class CandidatPieceInline(admin
.TabularInline
):
200 model
= CandidatPiece
201 fields
= ('candidat', 'nom', 'path',)
205 class CandidatEvaluationInlineFormSet(BaseInlineFormSet
):
207 Empêche la suppression d'une évaluation pour le CandidatEvaluationInline
209 def __init__(self
, *args
, **kwargs
):
210 super(CandidatEvaluationInlineFormSet
, self
).__init__(*args
, **kwargs
)
211 self
.can_delete
= False
213 class CandidatEvaluationInline(admin
.TabularInline
):
214 model
= CandidatEvaluation
215 fields
= ('evaluateur', 'note', 'commentaire')
218 formset
= CandidatEvaluationInlineFormSet
221 def get_readonly_fields(self
, request
, obj
=None):
223 Empêche la modification des évaluations
226 return self
.readonly_fields
+('evaluateur', 'note', 'commentaire')
227 return self
.readonly_fields
229 class CandidatAdmin(VersionAdmin
):
230 exclude
= ('actif', )
231 list_editable
= ('statut', )
232 list_display
= ('nom', 'prenom', 'offre_emploi',
233 'voir_offre_emploi', 'calculer_moyenne',
234 'afficher_candidat', '_date_creation', 'statut', )
235 list_filter
= ('offre_emploi', 'offre_emploi__region', 'statut', )
239 'fields': ('offre_emploi', )
241 ('Informations personnelles', {
242 'fields': ('prenom','nom','genre', 'nationalite',
243 'situation_famille', 'nombre_dependant',)
246 'fields': ('telephone', 'email', 'adresse', 'ville',
247 'etat_province', 'code_postal', 'pays', )
249 ('Informations professionnelles', {
250 'fields': ('niveau_diplome','employeur_actuel',
251 'poste_actuel', 'domaine_professionnel',)
254 'fields': ('statut', )
259 CandidatEvaluationInline
,
262 actions
= ['envoyer_courriel_candidats']
264 def _date_creation(self
, obj
):
265 return obj
.date_creation
266 _date_creation
.order_field
= "date_creation"
267 _date_creation
.short_description
= "Date de création"
269 ### Actions à afficher
270 def get_actions(self
, request
):
271 actions
= super(CandidatAdmin
, self
).get_actions(request
)
272 del actions
['delete_selected']
275 ### Envoyer un courriel à des candidats
276 def envoyer_courriel_candidats(modeladmin
, obj
, candidats
):
277 selected
= obj
.POST
.getlist(admin
.ACTION_CHECKBOX_NAME
)
279 return HttpResponseRedirect(reverse('selectionner_template')+
280 "?ids=%s" % (",".join(selected
)))
281 envoyer_courriel_candidats
.short_description
= u
'Envoyer courriel'
283 ### Évaluer un candidat
284 def evaluer_candidat(self
, obj
):
285 return "<a href='%s?candidat__id__exact=%s'>Évaluer le candidat</a>" % \
286 (reverse('admin:recrutement_candidatevaluation_changelist'),
288 evaluer_candidat
.allow_tags
= True
289 evaluer_candidat
.short_description
= 'Évaluation'
291 ### Afficher un candidat
292 def afficher_candidat(self
, obj
):
293 return "<a href='%s'>Voir le candidat</a>" % \
294 (reverse('admin:recrutement_proxycandidat_change', args
=(obj
.id,)))
295 afficher_candidat
.allow_tags
= True
296 afficher_candidat
.short_description
= u
'Détails du candidat'
298 ### Voir l'offre d'emploi
299 def voir_offre_emploi(self
, obj
):
300 return "<a href='%s'>Voir l'offre d'emploi</a>" % \
301 (reverse('admin:recrutement_proxyoffreemploi_change',
302 args
=(obj
.offre_emploi
.id,)))
303 voir_offre_emploi
.allow_tags
= True
304 voir_offre_emploi
.short_description
= "Afficher l'offre d'emploi"
306 ### Calculer la moyenne des notes
307 def calculer_moyenne(self
, obj
):
308 evaluations
= CandidatEvaluation
.objects
.filter(candidat
=obj
)
310 notes
= [evaluation
.note
for evaluation
in evaluations \
311 if evaluation
.note
is not None]
314 moyenne_votes
= float(sum(notes
)) / len(notes
)
316 moyenne_votes
= "Non disponible"
318 totales
= len(evaluations
)
321 if obj
.statut
== 'REC':
322 if totales
== faites
:
324 elif faites
> 0 and float(totales
) / float(faites
) >= 2:
331 return """<span style="color: %s;">%s (%s/%s)</span>""" % (color
, moyenne_votes
, faites
, totales
)
332 calculer_moyenne
.allow_tags
= True
333 calculer_moyenne
.short_description
= "Notation"
335 ### Permissions add, delete, change
336 def has_add_permission(self
, request
):
337 user_groupes
= request
.user
.groups
.all()
338 if request
.user
.is_superuser
is True or \
339 grp_drh_recrutement
in user_groupes
or \
340 grp_directeurs_bureau_recrutement
in user_groupes
or \
341 grp_administrateurs_recrutement
in user_groupes
:
345 def has_delete_permission(self
, request
, obj
=None):
346 user_groupes
= request
.user
.groups
.all()
347 if request
.user
.is_superuser
is True or \
348 grp_drh_recrutement
in user_groupes
or \
349 grp_directeurs_bureau_recrutement
in user_groupes
or \
350 grp_administrateurs_recrutement
in user_groupes
:
354 def has_change_permission(self
, request
, obj
=None):
355 user_groupes
= request
.user
.groups
.all()
356 if request
.user
.is_superuser
is True or \
357 grp_correspondants_rh_recrutement
in user_groupes
or \
358 grp_drh_recrutement
in user_groupes
or \
359 grp_directeurs_bureau_recrutement
in user_groupes
or \
360 grp_administrateurs_recrutement
in user_groupes
:
365 def queryset(self
, request
):
367 Spécifie un queryset limité, autrement Django exécute un
368 select_related() sans paramètre, ce qui a pour effet de charger tous
369 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
370 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
371 génération d'une requête infinie.
374 qs
= self
.model
._default_manager
.get_query_set()
375 user_groupes
= request
.user
.groups
.all()
376 if grp_drh_recrutement
in user_groupes
:
377 return qs
.select_related('candidats')
379 if grp_directeurs_bureau_recrutement
in user_groupes
or \
380 grp_correspondants_rh_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
:
387 evaluateur
= Evaluateur
.objects
.get(user
=request
.user
)
388 candidat_ids
= [e
.candidat
.id for e
in
389 CandidatEvaluation
.objects
.filter(evaluateur
=evaluateur
)]
390 return qs
.select_related('candidats').filter(id__in
=candidat_ids
)
394 class ProxyCandidatAdmin(CandidatAdmin
):
396 readonly_fields
= ('statut', 'offre_emploi', 'prenom', 'nom',
397 'genre', 'nationalite', 'situation_famille',
398 'nombre_dependant', 'telephone', 'email', 'adresse',
399 'ville', 'etat_province', 'code_postal', 'pays',
400 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
401 'domaine_professionnel', 'pieces_jointes',)
404 'fields': ('offre_emploi', )
406 ('Informations personnelles', {
407 'fields': ('prenom','nom','genre', 'nationalite',
408 'situation_famille', 'nombre_dependant',)
411 'fields': ('telephone', 'email', 'adresse', 'ville',
412 'etat_province', 'code_postal', 'pays', )
414 ('Informations professionnelles', {
415 'fields': ('niveau_diplome','employeur_actuel',
416 'poste_actuel', 'domaine_professionnel',)
421 ### Lieu de redirection après le change
422 def response_change(self
, request
, obj
):
423 response
= super(ProxyCandidatAdmin
, self
).response_change(request
, obj
)
424 user_groupes
= request
.user
.groups
.all()
425 return HttpResponseRedirect(reverse\
426 ('admin:recrutement_proxycandidat_changelist'))
428 ### Permissions add, delete, change
429 def has_add_permission(self
, request
):
432 def has_delete_permission(self
, request
, obj
=None):
435 def has_change_permission(self
, request
, obj
=None):
436 user_groupes
= request
.user
.groups
.all()
437 if request
.user
.is_superuser
is True or \
438 grp_drh_recrutement
in user_groupes
or \
439 grp_evaluateurs_recrutement
in user_groupes
or \
440 grp_directeurs_bureau_recrutement
in user_groupes
or \
441 grp_administrateurs_recrutement
in user_groupes
:
445 class CandidatPieceAdmin(admin
.ModelAdmin
):
446 list_display
= ('nom', 'candidat', )
449 def queryset(self
, request
):
451 Spécifie un queryset limité, autrement Django exécute un
452 select_related() sans paramètre, ce qui a pour effet de charger tous
453 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
454 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
455 génération d'une requête infinie.
456 Affiche la liste de candidats que si le user connecté
457 possède un Evaluateur
459 qs
= self
.model
._default_manager
.get_query_set()
460 return qs
.select_related('candidat')
462 class EvaluateurAdmin(VersionAdmin
):
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 request
.user
.is_superuser
is True or \
479 grp_drh_recrutement
in user_groupes
or \
480 grp_directeurs_bureau_recrutement
in user_groupes
or \
481 grp_administrateurs_recrutement
in user_groupes
:
485 def has_delete_permission(self
, request
, obj
=None):
486 user_groupes
= request
.user
.groups
.all()
487 if request
.user
.is_superuser
is True or \
488 grp_drh_recrutement
in user_groupes
or \
489 grp_directeurs_bureau_recrutement
in user_groupes
or \
490 grp_administrateurs_recrutement
in user_groupes
:
494 def has_change_permission(self
, request
, obj
=None):
495 user_groupes
= request
.user
.groups
.all()
496 if request
.user
.is_superuser
is True or \
497 grp_drh_recrutement
in user_groupes
or \
498 grp_directeurs_bureau_recrutement
in user_groupes
or \
499 grp_administrateurs_recrutement
in user_groupes
:
503 class CandidatEvaluationAdmin(admin
.ModelAdmin
):
504 search_fields
= ('candidat__nom', 'candidat__prenom' )
505 list_display
= ('_candidat', '_statut', '_offre_emploi', 'evaluateur', '_note',
507 readonly_fields
= ('candidat', 'evaluateur')
508 list_filter
= ('candidat__statut', 'candidat__offre_emploi',)
510 ('Évaluation du candidat', {
511 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
515 ### Actions à afficher
516 def get_actions(self
, request
):
517 actions
= super(CandidatEvaluationAdmin
, self
).get_actions(request
)
518 del actions
['delete_selected']
522 def _note(self
, obj
):
524 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
525 un lien pour Évaluer le candidat.
526 Sinon afficher la note.
529 return "<a href='%s'>Candidat non évalué</a>" % \
530 (reverse('admin:recrutement_candidatevaluation_change',
532 return "<a href='%s'>%s</a>" % \
533 (reverse('admin:recrutement_candidatevaluation_change',
534 args
=(obj
.id,)), obj
.note
)
535 _note
.allow_tags
= True
536 _note
.short_description
= "Note"
537 _note
.admin_order_field
= 'note'
539 def _statut(self
, obj
):
540 return obj
.candidat
.get_statut_display()
541 _statut
.order_field
= 'candidat__statut'
542 _statut
.short_description
= 'Statut'
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 def has_add_permission(self
, request
):
577 def has_delete_permission(self
, request
, obj
=None):
580 def has_change_permission(self
, request
, obj
=None):
582 Permettre la visualisation dans la changelist
583 mais interdire l'accès à modifier l'objet si l'évaluateur n'est pas
589 if request
.user
.is_superuser
is True:
592 return request
.user
== obj
.evaluateur
.user
595 def queryset(self
, request
):
597 Afficher uniquement les évaluations de l'évaluateur, sauf si
598 l'utilisateur est dans les groupes suivants.
600 qs
= self
.model
._default_manager
.get_query_set().select_related('offre_emploi')
601 user_groupes
= request
.user
.groups
.all()
603 if grp_drh_recrutement
in user_groupes
or \
604 grp_correspondants_rh_recrutement
in user_groupes
or \
605 grp_directeurs_bureau_recrutement
in user_groupes
or \
606 grp_administrateurs_recrutement
in user_groupes
:
609 evaluateur
= Evaluateur
.objects
.get(user
=request
.user
)
610 candidats_evaluations
= \
611 CandidatEvaluation
.objects
.filter(evaluateur
=evaluateur
,
612 candidat__statut__in
=('REC', ))
613 candidats_evaluations_ids
= [ce
.id for ce
in candidats_evaluations
]
614 return qs
.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
)