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
17 from recrutement
.forms
import *
20 IMPLANTATIONS_CENTRALES
= [15, 19]
22 class OffreEmploiAdmin(VersionAdmin
):
23 date_hierarchy
= 'date_creation'
24 list_display
= ('nom', 'date_limite', 'region', 'statut',
25 'est_affiche', '_candidatsList', )
26 exclude
= ('actif', 'poste_nom', 'resume',)
27 list_filter
= ('statut',)
28 actions
= ['affecter_evaluateurs_offre_emploi', ]
29 form
= OffreEmploiForm
31 ### Actions à afficher
32 def get_actions(self
, request
):
33 actions
= super(OffreEmploiAdmin
, self
).get_actions(request
)
34 del actions
['delete_selected']
37 ### Affecter un évaluateurs à des offres d'emploi
38 def affecter_evaluateurs_offre_emploi(modeladmin
, obj
, candidats
):
39 selected
= obj
.POST
.getlist(admin
.ACTION_CHECKBOX_NAME
)
41 return HttpResponseRedirect(reverse('affecter_evaluateurs_offre_emploi')+
42 "?ids=%s" % (",".join(selected
)))
43 affecter_evaluateurs_offre_emploi
.short_description
= u
'Affecter évaluateur(s)'
45 ### Afficher la liste des candidats pour l'offre d'emploi
46 def _candidatsList(self
, obj
):
47 return "<a href='%s?offre_emploi__id__exact=%s'>Voir les candidats \
48 </a>" % (reverse('admin:recrutement_candidat_changelist'), obj
.id)
49 _candidatsList
.allow_tags
= True
50 _candidatsList
.short_description
= "Afficher la liste des candidats"
53 def get_form(self
, request
, obj
=None, **kwargs
):
54 form
= super(OffreEmploiAdmin
, self
).get_form(request
, obj
, **kwargs
)
55 employe
= get_emp(request
.user
)
56 user_groupes
= request
.user
.groups
.all()
60 if form
.declared_fields
.has_key('region'):
61 region_field
= form
.declared_fields
['region']
63 region_field
= form
.base_fields
['region']
65 if grp_drh_recrutement
in user_groupes
:
66 region_field
.queryset
= Region
.objects
.all()
68 region_field
.queryset
= Region
.objects
.\
69 filter(id=employe
.implantation
.region
.id)
72 if form
.declared_fields
.has_key('poste'):
73 poste_field
= form
.declared_fields
['poste']
75 poste_field
= form
.base_fields
['poste']
77 if grp_drh_recrutement
in user_groupes
:
78 poste_field
.queryset
= rh
.Poste
.objects
.all()
80 poste_field
.queryset
= rh
.Poste
.objects
.\
81 filter(implantation__region
=employe
.implantation
.region
).\
82 exclude(implantation__in
=IMPLANTATIONS_CENTRALES
)
85 if form
.declared_fields
.has_key('bureau'):
86 bureau_field
= form
.declared_fields
['bureau']
88 bureau_field
= form
.base_fields
['bureau']
90 if grp_drh_recrutement
in user_groupes
:
91 bureau_field
.queryset
= Bureau
.objects
.all()
93 bureau_field
.queryset
= Bureau
.objects
.\
94 filter(region
=employe
.implantation
.region
)
99 def queryset(self
, request
):
100 qs
= self
.model
._default_manager
.get_query_set().select_related('offre_emploi')
101 user_groupes
= request
.user
.groups
.all()
102 if grp_drh_recrutement
in user_groupes
:
105 if grp_directeurs_bureau_recrutement
in user_groupes
or \
106 grp_correspondants_rh_recrutement
in user_groupes
or \
107 grp_administrateurs_recrutement
in user_groupes
:
108 employe
= get_emp(request
.user
)
109 return qs
.filter(region
=employe
.implantation
.region
)
111 if Evaluateur
.objects
.filter(user
=request
.user
).exists():
112 evaluateur
= Evaluateur
.objects
.get(user
=request
.user
)
113 offre_ids
= [e
.candidat
.offre_emploi_id
for e
in
114 CandidatEvaluation
.objects
.select_related('candidat').filter(evaluateur
=evaluateur
)]
115 return qs
.filter(id__in
=offre_ids
)
119 ### Permission add, delete, change
120 def has_add_permission(self
, request
):
121 user_groupes
= request
.user
.groups
.all()
122 if request
.user
.is_superuser
is True or \
123 grp_drh_recrutement
in user_groupes
or \
124 grp_directeurs_bureau_recrutement
in user_groupes
or \
125 grp_administrateurs_recrutement
in user_groupes
:
129 def has_delete_permission(self
, request
, obj
=None):
130 user_groupes
= request
.user
.groups
.all()
131 if request
.user
.is_superuser
is True or \
132 grp_drh_recrutement
in user_groupes
or \
133 grp_directeurs_bureau_recrutement
in user_groupes
or \
134 grp_administrateurs_recrutement
in user_groupes
:
138 def has_change_permission(self
, request
, obj
=None):
139 user_groupes
= request
.user
.groups
.all()
140 if request
.user
.is_superuser
is True or \
141 grp_drh_recrutement
in user_groupes
or \
142 grp_directeurs_bureau_recrutement
in user_groupes
or \
143 grp_administrateurs_recrutement
in user_groupes
:
147 class ProxyOffreEmploiAdmin(OffreEmploiAdmin
):
148 list_display
= ('nom', 'date_limite', 'region', 'statut',
150 readonly_fields
= ('description', 'bureau', 'duree_affectation',
151 'renumeration', 'debut_affectation', 'lieu_affectation',
152 'nom', 'resume', 'date_limite', 'region', 'poste')
157 ('Description générale', {
158 'fields': ('description', 'date_limite', )
161 'fields': ('lieu_affectation', 'bureau', 'region', 'poste',)
164 'fields': ('debut_affectation', 'duree_affectation',
171 ### Lieu de redirection après le change
172 def response_change(self
, request
, obj
):
173 return HttpResponseRedirect(reverse\
174 ('admin:recrutement_proxyoffreemploi_changelist'))
177 def get_form(self
, request
, obj
=None, **kwargs
):
178 form
= super(OffreEmploiAdmin
, self
).get_form(request
, obj
, **kwargs
)
181 ### Permissions add, delete, change
182 def has_add_permission(self
, request
):
185 def has_delete_permission(self
, request
, obj
=None):
188 def has_change_permission(self
, request
, obj
=None):
191 class CandidatPieceInline(admin
.TabularInline
):
192 model
= CandidatPiece
193 fields
= ('candidat', 'nom', 'path',)
197 class ReadOnlyCandidatPieceInline(CandidatPieceInline
):
198 readonly_fields
= ('candidat', 'nom', 'path', )
202 class CandidatEvaluationInlineFormSet(BaseInlineFormSet
):
204 Empêche la suppression d'une évaluation pour le CandidatEvaluationInline
206 def __init__(self
, *args
, **kwargs
):
207 super(CandidatEvaluationInlineFormSet
, self
).__init__(*args
, **kwargs
)
208 self
.can_delete
= False
210 class CandidatEvaluationInline(admin
.TabularInline
):
211 model
= CandidatEvaluation
212 fields
= ('evaluateur', 'note', 'commentaire')
215 formset
= CandidatEvaluationInlineFormSet
218 def get_readonly_fields(self
, request
, obj
=None):
220 Empêche la modification des évaluations
223 return self
.readonly_fields
+('evaluateur', 'note', 'commentaire')
224 return self
.readonly_fields
226 class CandidatAdmin(VersionAdmin
):
227 search_fields
= ('nom', 'prenom' )
228 exclude
= ('actif', )
229 list_editable
= ('statut', )
230 list_display
= ('nom', 'prenom', 'offre_emploi',
231 'voir_offre_emploi', 'calculer_moyenne',
232 'afficher_candidat', '_date_creation', 'statut', )
233 list_filter
= ('offre_emploi', 'offre_emploi__region', 'statut', )
237 'fields': ('offre_emploi', )
239 ('Informations personnelles', {
240 'fields': ('prenom','nom','genre', 'nationalite',
241 'situation_famille', 'nombre_dependant',)
244 'fields': ('telephone', 'email', 'adresse', 'ville',
245 'etat_province', 'code_postal', 'pays', )
247 ('Informations professionnelles', {
248 'fields': ('niveau_diplome','employeur_actuel',
249 'poste_actuel', 'domaine_professionnel',)
252 'fields': ('statut', )
257 CandidatEvaluationInline
,
260 actions
= ['envoyer_courriel_candidats']
262 def _date_creation(self
, obj
):
263 return obj
.date_creation
264 _date_creation
.order_field
= "date_creation"
265 _date_creation
.short_description
= "Date de création"
267 ### Actions à afficher
268 def get_actions(self
, request
):
269 actions
= super(CandidatAdmin
, self
).get_actions(request
)
270 del actions
['delete_selected']
273 ### Envoyer un courriel à des candidats
274 def envoyer_courriel_candidats(modeladmin
, obj
, candidats
):
275 selected
= obj
.POST
.getlist(admin
.ACTION_CHECKBOX_NAME
)
277 return HttpResponseRedirect(reverse('selectionner_template')+
278 "?ids=%s" % (",".join(selected
)))
279 envoyer_courriel_candidats
.short_description
= u
'Envoyer courriel'
281 ### Évaluer un candidat
282 def evaluer_candidat(self
, obj
):
283 return "<a href='%s?candidat__id__exact=%s'>Évaluer le candidat</a>" % \
284 (reverse('admin:recrutement_candidatevaluation_changelist'),
286 evaluer_candidat
.allow_tags
= True
287 evaluer_candidat
.short_description
= 'Évaluation'
289 ### Afficher un candidat
290 def afficher_candidat(self
, obj
):
291 return "<a href='%s'>Voir le candidat</a>" % \
292 (reverse('admin:recrutement_proxycandidat_change', args
=(obj
.id,)))
293 afficher_candidat
.allow_tags
= True
294 afficher_candidat
.short_description
= u
'Détails du candidat'
296 ### Voir l'offre d'emploi
297 def voir_offre_emploi(self
, obj
):
298 return "<a href='%s'>Voir l'offre d'emploi</a>" % \
299 (reverse('admin:recrutement_proxyoffreemploi_change',
300 args
=(obj
.offre_emploi
.id,)))
301 voir_offre_emploi
.allow_tags
= True
302 voir_offre_emploi
.short_description
= "Afficher l'offre d'emploi"
304 ### Calculer la moyenne des notes
305 def calculer_moyenne(self
, obj
):
306 evaluations
= CandidatEvaluation
.objects
.filter(candidat
=obj
)
308 notes
= [evaluation
.note
for evaluation
in evaluations \
309 if evaluation
.note
is not None]
312 moyenne_votes
= float(sum(notes
)) / len(notes
)
314 moyenne_votes
= "Non disponible"
316 totales
= len(evaluations
)
319 if obj
.statut
== 'REC':
320 if totales
== faites
:
322 elif faites
> 0 and float(totales
) / float(faites
) >= 2:
329 return """<span style="color: %s;">%s (%s/%s)</span>""" % (color
, moyenne_votes
, faites
, totales
)
330 calculer_moyenne
.allow_tags
= True
331 calculer_moyenne
.short_description
= "Notation"
333 ### Permissions add, delete, change
334 def has_add_permission(self
, request
):
335 user_groupes
= request
.user
.groups
.all()
336 if request
.user
.is_superuser
is True or \
337 grp_drh_recrutement
in user_groupes
or \
338 grp_directeurs_bureau_recrutement
in user_groupes
or \
339 grp_administrateurs_recrutement
in user_groupes
:
343 def has_delete_permission(self
, request
, obj
=None):
344 user_groupes
= request
.user
.groups
.all()
345 if request
.user
.is_superuser
is True or \
346 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 request
.user
.is_superuser
is True or \
355 grp_correspondants_rh_recrutement
in user_groupes
or \
356 grp_drh_recrutement
in user_groupes
or \
357 grp_directeurs_bureau_recrutement
in user_groupes
or \
358 grp_administrateurs_recrutement
in user_groupes
:
363 def queryset(self
, request
):
365 Spécifie un queryset limité, autrement Django exécute un
366 select_related() sans paramètre, ce qui a pour effet de charger tous
367 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
368 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
369 génération d'une requête infinie.
372 qs
= self
.model
._default_manager
.get_query_set()
373 user_groupes
= request
.user
.groups
.all()
374 if grp_drh_recrutement
in user_groupes
:
375 return qs
.select_related('candidats')
377 if grp_directeurs_bureau_recrutement
in user_groupes
or \
378 grp_correspondants_rh_recrutement
in user_groupes
or \
379 grp_administrateurs_recrutement
in user_groupes
:
380 employe
= get_emp(request
.user
)
381 return qs
.select_related('candidats').\
382 filter(offre_emploi__region
=employe
.implantation
.region
)
384 if Evaluateur
.objects
.filter(user
=request
.user
).exists():
385 evaluateur
= Evaluateur
.objects
.get(user
=request
.user
)
386 candidat_ids
= [e
.candidat
.id for e
in
387 CandidatEvaluation
.objects
.filter(evaluateur
=evaluateur
)]
388 return qs
.select_related('candidats').filter(id__in
=candidat_ids
)
392 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',)
417 inlines
= (CandidatEvaluationInline
, )
419 def has_add_permission(self
, request
):
422 def has_delete_permission(self
, request
, obj
=None):
425 def has_change_permission(self
, request
, obj
=None):
428 class CandidatPieceAdmin(admin
.ModelAdmin
):
429 list_display
= ('nom', 'candidat', )
432 def queryset(self
, request
):
434 Spécifie un queryset limité, autrement Django exécute un
435 select_related() sans paramètre, ce qui a pour effet de charger tous
436 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
437 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
438 génération d'une requête infinie.
439 Affiche la liste de candidats que si le user connecté
440 possède un Evaluateur
442 qs
= self
.model
._default_manager
.get_query_set()
443 return qs
.select_related('candidat')
445 class EvaluateurAdmin(VersionAdmin
):
452 ### Actions à afficher
453 def get_actions(self
, request
):
454 actions
= super(EvaluateurAdmin
, self
).get_actions(request
)
455 del actions
['delete_selected']
458 ### Permissions add, delete, change
459 def has_add_permission(self
, request
):
460 user_groupes
= request
.user
.groups
.all()
461 if request
.user
.is_superuser
is True or \
462 grp_drh_recrutement
in user_groupes
or \
463 grp_directeurs_bureau_recrutement
in user_groupes
or \
464 grp_administrateurs_recrutement
in user_groupes
:
468 def has_delete_permission(self
, request
, obj
=None):
469 user_groupes
= request
.user
.groups
.all()
470 if request
.user
.is_superuser
is True or \
471 grp_drh_recrutement
in user_groupes
or \
472 grp_directeurs_bureau_recrutement
in user_groupes
or \
473 grp_administrateurs_recrutement
in user_groupes
:
477 def has_change_permission(self
, request
, obj
=None):
478 user_groupes
= request
.user
.groups
.all()
479 if request
.user
.is_superuser
is True or \
480 grp_drh_recrutement
in user_groupes
or \
481 grp_directeurs_bureau_recrutement
in user_groupes
or \
482 grp_administrateurs_recrutement
in user_groupes
:
486 class CandidatEvaluationAdmin(admin
.ModelAdmin
):
487 search_fields
= ('candidat__nom', 'candidat__prenom' )
488 list_display
= ('_candidat', '_statut', '_offre_emploi', 'evaluateur', '_note',
490 readonly_fields
= ('candidat', 'evaluateur')
491 list_filter
= ('candidat__statut', 'candidat__offre_emploi',)
493 ('Évaluation du candidat', {
494 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
498 ### Actions à afficher
499 def get_actions(self
, request
):
500 actions
= super(CandidatEvaluationAdmin
, self
).get_actions(request
)
501 del actions
['delete_selected']
505 def _note(self
, obj
):
507 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
508 un lien pour Évaluer le candidat.
509 Sinon afficher la note.
512 return "<a href='%s'>Candidat non évalué</a>" % \
513 (reverse('admin:recrutement_candidatevaluation_change',
515 return "<a href='%s'>%s</a>" % \
516 (reverse('admin:recrutement_candidatevaluation_change',
517 args
=(obj
.id,)), obj
.note
)
518 _note
.allow_tags
= True
519 _note
.short_description
= "Note"
520 _note
.admin_order_field
= 'note'
522 def _statut(self
, obj
):
523 return obj
.candidat
.get_statut_display()
524 _statut
.order_field
= 'candidat__statut'
525 _statut
.short_description
= 'Statut'
528 ### Lien en lecture seule vers le candidat
529 def _candidat(self
, obj
):
530 return "<a href='%s'>%s</a>" \
531 % (reverse('admin:recrutement_proxycandidat_change',
532 args
=(obj
.candidat
.id,)), obj
.candidat
)
533 _candidat
.allow_tags
= True
534 _candidat
.short_description
= 'Candidat'
536 ### Afficher commentaire
537 def _commentaire(self
, obj
):
539 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
540 dans le champ commentaire, Aucun au lieu de (None)
541 Sinon afficher la note.
543 if obj
.commentaire
is None:
545 return obj
.commentaire
546 _commentaire
.allow_tags
= True
547 _commentaire
.short_description
= "Commentaire"
549 ### Afficher offre d'emploi
550 def _offre_emploi(self
, obj
):
551 return "<a href='%s'>%s</a>" % \
552 (reverse('admin:recrutement_proxyoffreemploi_change',
553 args
=(obj
.candidat
.offre_emploi
.id,)), obj
.candidat
.offre_emploi
)
554 _offre_emploi
.allow_tags
= True
555 _offre_emploi
.short_description
= "Voir offre d'emploi"
557 def has_add_permission(self
, request
):
560 def has_delete_permission(self
, request
, obj
=None):
563 def has_change_permission(self
, request
, obj
=None):
565 Permettre la visualisation dans la changelist
566 mais interdire l'accès à modifier l'objet si l'évaluateur n'est pas
569 user_groupes
= request
.user
.groups
.all()
571 if 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
:
575 is_recrutement
= True
577 is_recrutement
= False
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
)
618 class CourrielTemplateAdmin(VersionAdmin
):
619 ### Actions à afficher
620 def get_actions(self
, request
):
621 actions
= super(CourrielTemplateAdmin
, self
).get_actions(request
)
622 del actions
['delete_selected']
625 admin
.site
.register(OffreEmploi
, OffreEmploiAdmin
)
626 admin
.site
.register(ProxyOffreEmploi
, ProxyOffreEmploiAdmin
)
627 admin
.site
.register(Candidat
, CandidatAdmin
)
628 admin
.site
.register(ProxyCandidat
, ProxyCandidatAdmin
)
629 admin
.site
.register(CandidatEvaluation
, CandidatEvaluationAdmin
)
630 admin
.site
.register(Evaluateur
, EvaluateurAdmin
)
631 admin
.site
.register(CourrielTemplate
, CourrielTemplateAdmin
)