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
.shortcuts
import get_object_or_404
7 from django
.core
.files
.storage
import default_storage
8 from django
.forms
.models
import BaseInlineFormSet
10 from reversion
.admin
import VersionAdmin
11 from datamaster_modeles
.models
import Employe
, Implantation
, Region
13 from project
.rh
import models
as rh
14 from recrutement
.models
import *
15 from recrutement
.workflow
import grp_evaluateurs_recrutement
, \
16 grp_drh_recrutement
, grp_directeurs_bureau_recrutement
, \
17 grp_administrateurs_recrutement
18 from recrutement
.forms
import *
20 from project
.dae
.utils
import get_employe_from_user
as get_emp
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 def get_actions(self
, request
):
47 actions
= super(OffreEmploiAdmin
, self
).get_actions(request
)
48 del actions
['delete_selected']
51 # Affecter un évaluateurs à des offres d'emploi
52 def affecter_evaluateurs_offre_emploi(modeladmin
, obj
, candidats
):
53 selected
= obj
.POST
.getlist(admin
.ACTION_CHECKBOX_NAME
)
55 return HttpResponseRedirect(reverse('affecter_evaluateurs_offre_emploi')+
56 "?ids=%s" % (",".join(selected
)))
57 affecter_evaluateurs_offre_emploi
.short_description
= u
'Affecter évaluateur(s)'
59 # Afficher la liste des candidats pour l'offre d'emploi
60 def _candidatsList(self
, obj
):
61 return "<a href='%s?offre_emploi__id__exact=%s'>Voir les candidats \
62 </a>" % (reverse('admin:recrutement_candidat_changelist'), obj
.id)
63 _candidatsList
.allow_tags
= True
64 _candidatsList
.short_description
= "Afficher la liste des candidats"
66 def get_form(self
, request
, obj
=None, **kwargs
):
67 form
= super(OffreEmploiAdmin
, self
).get_form(request
, obj
, **kwargs
)
68 employe
= get_emp(request
.user
)
69 user_groupes
= request
.user
.groups
.all()
70 if not grp_drh_recrutement
in user_groupes
and \
71 not request
.user
.is_superuser
:
72 if form
.declared_fields
.has_key('region'):
73 region_field
= form
.declared_fields
['region']
75 region_field
= form
.base_fields
['region']
77 region_ids
= [g
.id for g
in user
.regions
.all()]
78 region_field
.queryset
= Region
.objects
.filter(id__in
=region_ids
)
81 def queryset(self
, request
):
82 qs
= self
.model
._default_manager
.get_query_set()
83 # Si user est superuser afficher toutes les offres d'emploi
84 user_groupes
= request
.user
.groups
.all()
85 if not grp_drh_recrutement
in user_groupes
and \
86 not grp_directeurs_bureau_recrutement
in user_groupes
and \
87 not grp_administrateurs_recrutement
in user_groupes
and \
88 not request
.user
.is_superuser
:
90 Si le user n'est ni un évaluateur ni un administrateur régional,
94 if grp_evaluateurs_recrutement
in user_groupes
:
96 user
= Evaluateur
.objects
.get(user
=request
.user
)
97 except Evaluateur
.DoesNotExist
:
102 if type(user
) is Evaluateur
:
103 candidats
= [g
for g
in user
.candidats
.all()]
104 offre_emploi_ids
= [c
.offre_emploi
.id for c
in candidats
]
105 return qs
.select_related('offre_emploi').\
106 filter(id__in
=offre_emploi_ids
)
108 return qs
.select_related('offre_emploi')
110 def has_add_permission(self
, request
):
111 user_groupes
= request
.user
.groups
.all()
112 if grp_drh_recrutement
in user_groupes
or \
113 grp_directeurs_bureau_recrutement
in user_groupes
or \
114 grp_administrateurs_recrutement
in user_groupes
or \
115 request
.user
.is_superuser
:
119 def has_change_permission(self
, request
, obj
=None):
120 user_groupes
= request
.user
.groups
.all()
121 if grp_drh_recrutement
in user_groupes
or \
122 grp_directeurs_bureau_recrutement
in user_groupes
or \
123 grp_administrateurs_recrutement
in user_groupes
or \
124 request
.user
.is_superuser
:
128 class ProxyOffreEmploiAdmin(OffreEmploiAdmin
):
129 list_display
= ('nom', 'resume', 'date_limite', 'region', 'statut',
131 readonly_fields
= ('description', 'bureau', 'duree_affectation',
132 'renumeration', 'debut_affectation', 'lieu_affectation',
133 'nom', 'resume', 'date_limite', 'region')
138 ('Description générale', {
139 'fields': ('resume','description', 'date_limite', )
142 'fields': ('lieu_affectation', 'bureau', 'region', )
145 'fields': ('debut_affectation', 'duree_affectation',
150 def get_actions(self
, request
):
151 actions
= super(ProxyOffreEmploiAdmin
, self
).get_actions(request
)
152 del actions
['affecter_evaluateurs_offre_emploi']
155 def response_change(self
, request
, obj
):
156 response
= super(ProxyOffreEmploiAdmin
, self
).response_change(request
,
158 user_groupes
= request
.user
.groups
.all()
159 if grp_drh_recrutement
in user_groupes
or \
160 grp_directeurs_bureau_recrutement
in user_groupes
or \
161 grp_administrateurs_recrutement
in user_groupes
or \
162 request
.user
.is_superuser
:
163 return HttpResponseRedirect(reverse\
164 ('admin:recrutement_offreemploi_changelist'))
165 return HttpResponseRedirect(reverse\
166 ('admin:recrutement_proxyoffreemploi_changelist'))
168 def has_add_permission(self
, request
):
171 def has_delete_permission(self
, request
, obj
=None):
174 def has_change_permission(self
, request
, obj
=None):
175 user_groupes
= request
.user
.groups
.all()
176 if grp_evaluateurs_recrutement
in user_groupes
or \
177 grp_drh_recrutement
in user_groupes
or \
178 grp_directeurs_bureau_recrutement
in user_groupes
or \
179 grp_administrateurs_recrutement
in user_groupes
or \
180 request
.user
.is_superuser
:
184 class CandidatPieceInline(admin
.TabularInline
):
185 model
= CandidatPiece
186 fields
= ('candidat', 'nom', 'path',)
190 class CandidatEvaluationInlineFormSet(BaseInlineFormSet
):
192 Empêche la suppression d'une évaluation pour le CandidatEvaluationInline
194 def __init__(self
, *args
, **kwargs
):
195 super(CandidatEvaluationInlineFormSet
, self
).__init__(*args
, **kwargs
)
196 self
.can_delete
= False
198 class CandidatEvaluationInline(admin
.TabularInline
):
199 model
= CandidatEvaluation
200 fields
= ('evaluateur', 'note', 'commentaire')
203 formset
= CandidatEvaluationInlineFormSet
205 def get_readonly_fields(self
, request
, obj
=None):
207 Empêche la modification des évaluations
210 return self
.readonly_fields
+('evaluateur', 'note', 'commentaire')
211 return self
.readonly_fields
213 class CandidatAdmin(VersionAdmin
):
214 exclude
= ('actif', )
215 date_hierarchy
= 'date_creation'
216 list_display
= ('nom', 'prenom', 'offre_emploi','statut',
217 'voir_offre_emploi', 'calculer_moyenne',
218 'afficher_candidat',)
219 list_filter
= ('offre_emploi', )
223 'fields': ('offre_emploi', )
225 ('Informations personnelles', {
226 'fields': ('prenom','nom','genre', 'nationalite',
227 'situation_famille', 'nombre_dependant',)
230 'fields': ('telephone', 'email', 'adresse', 'ville',
231 'etat_province', 'code_postal', 'pays', )
233 ('Informations professionnelles', {
234 'fields': ('niveau_diplome','employeur_actuel',
235 'poste_actuel', 'domaine_professionnel',)
238 'fields': ('statut', )
243 CandidatEvaluationInline
,
246 actions
= ['envoyer_courriel_candidats']
248 def get_actions(self
, request
):
249 actions
= super(CandidatAdmin
, self
).get_actions(request
)
250 del actions
['delete_selected']
253 # Envoyer un courriel à des candidats
254 def envoyer_courriel_candidats(modeladmin
, obj
, candidats
):
255 selected
= obj
.POST
.getlist(admin
.ACTION_CHECKBOX_NAME
)
257 return HttpResponseRedirect(reverse('selectionner_template')+
258 "?ids=%s" % (",".join(selected
)))
259 envoyer_courriel_candidats
.short_description
= u
'Envoyer courriel'
261 # Évaluer un candidat
262 def evaluer_candidat(self
, obj
):
263 return "<a href='%s?candidat__id__exact=%s'>Évaluer le candidat</a>" % \
264 (reverse('admin:recrutement_candidatevaluation_changelist'),
266 evaluer_candidat
.allow_tags
= True
267 evaluer_candidat
.short_description
= 'Évaluation'
269 # Afficher un candidat
270 def afficher_candidat(self
, obj
):
271 return "<a href='%s'>Voir le candidat</a>" % \
272 (reverse('admin:recrutement_proxycandidat_change', args
=(obj
.id,)))
273 afficher_candidat
.allow_tags
= True
274 afficher_candidat
.short_description
= u
'Détails du candidat'
276 # Voir l'offre d'emploi
277 def voir_offre_emploi(self
, obj
):
278 return "<a href='%s'>Voir l'offre d'emploi</a>" % \
279 (reverse('admin:recrutement_proxyoffreemploi_change',
280 args
=(obj
.offre_emploi
.id,)))
281 voir_offre_emploi
.allow_tags
= True
282 voir_offre_emploi
.short_description
= "Afficher l'offre d'emploi"
284 # Calculer la moyenne des notes
285 def calculer_moyenne(self
, obj
):
286 evaluations
= CandidatEvaluation
.objects
.filter(candidat
=obj
)
287 offre_emploi
= obj
.offre_emploi
289 notes
= [evaluation
.note
for evaluation
in evaluations
.all() \
290 if evaluation
.note
is not None]
293 moyenne_votes
= float(sum(notes
)) / len(notes
)
295 moyenne_votes
= "Non disponible"
297 calculer_moyenne
.allow_tags
= True
298 calculer_moyenne
.short_description
= "Moyenne des notes"
300 def has_add_permission(self
, request
):
301 user_groupes
= request
.user
.groups
.all()
302 if grp_drh_recrutement
in user_groupes
or \
303 grp_directeurs_bureau_recrutement
in user_groupes
or \
304 grp_administrateurs_recrutement
in user_groupes
or \
305 request
.user
.is_superuser
:
309 def has_delete_permission(self
, request
, obj
=None):
310 user_groupes
= request
.user
.groups
.all()
311 if grp_drh_recrutement
in user_groupes
or \
312 grp_directeurs_bureau_recrutement
in user_groupes
or \
313 grp_administrateurs_recrutement
in user_groupes
or \
314 request
.user
.is_superuser
:
318 def has_change_permission(self
, request
, obj
=None):
319 user_groupes
= request
.user
.groups
.all()
320 if grp_drh_recrutement
in user_groupes
or \
321 grp_directeurs_bureau_recrutement
in user_groupes
or \
322 grp_administrateurs_recrutement
in user_groupes
or \
323 request
.user
.is_superuser
:
327 def queryset(self
, request
):
329 Spécifie un queryset limité, autrement Django exécute un
330 select_related() sans paramètre, ce qui a pour effet de charger tous
331 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
332 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
333 génération d'une requête infinie.
336 qs
= self
.model
._default_manager
.get_query_set()
337 # Si user est superuser afficher tous les candidats
338 user_groupes
= request
.user
.groups
.all()
339 if not grp_drh_recrutement
in user_groupes
and \
340 not grp_directeurs_bureau_recrutement
in user_groupes
and \
341 not grp_administrateurs_recrutement
in user_groupes
and \
342 not request
.user
.is_superuser
:
343 # Si le user n'est ni un évaluateur ni un administrateur régional,
347 if grp_evaluateurs_recrutement
in user_groupes
:
349 user
= Evaluateur
.objects
.get(user
=request
.user
)
350 except Evaluateur
.DoesNotExist
:
354 ids
= [c
.id for c
in user
.candidats
.all()]
355 return qs
.select_related('candidats').filter(id__in
=ids
)
356 return qs
.select_related('candidats')
358 class ProxyCandidatAdmin(CandidatAdmin
):
359 readonly_fields
= ('statut', 'offre_emploi', 'prenom', 'nom',
360 'genre', 'nationalite', 'situation_famille',
361 'nombre_dependant', 'telephone', 'email', 'adresse',
362 'ville', 'etat_province', 'code_postal', 'pays',
363 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
364 'domaine_professionnel', 'pieces_jointes',)
367 'fields': ('offre_emploi', )
369 ('Informations personnelles', {
370 'fields': ('prenom','nom','genre', 'nationalite',
371 'situation_famille', 'nombre_dependant',)
374 'fields': ('telephone', 'email', 'adresse', 'ville',
375 'etat_province', 'code_postal', 'pays', )
377 ('Informations professionnelles', {
378 'fields': ('niveau_diplome','employeur_actuel',
379 'poste_actuel', 'domaine_professionnel',)
384 def response_change(self
, request
, obj
):
385 response
= super(ProxyCandidatAdmin
, self
).response_change(request
, obj
)
386 user_groupes
= request
.user
.groups
.all()
387 if grp_drh_recrutement
in user_groupes
or \
388 grp_directeurs_bureau_recrutement
in user_groupes
or \
389 grp_administrateurs_recrutement
in user_groupes
or \
390 request
.user
.is_superuser
:
391 return HttpResponseRedirect(reverse\
392 ('admin:recrutement_candidat_changelist'))
393 return HttpResponseRedirect(reverse\
394 ('admin:recrutement_proxycandidat_changelist'))
396 def has_add_permission(self
, request
):
399 def has_delete_permission(self
, request
, obj
=None):
402 def has_change_permission(self
, request
, obj
=None):
403 user_groupes
= request
.user
.groups
.all()
404 if grp_drh_recrutement
in user_groupes
or \
405 grp_evaluateurs_recrutement
in user_groupes
or \
406 grp_directeurs_bureau_recrutement
in user_groupes
or \
407 grp_administrateurs_recrutement
in user_groupes
or \
408 request
.user
.is_superuser
:
412 class CandidatPieceAdmin(admin
.ModelAdmin
):
413 list_display
= ('nom', 'candidat', )
415 def queryset(self
, request
):
417 Spécifie un queryset limité, autrement Django exécute un
418 select_related() sans paramètre, ce qui a pour effet de charger tous
419 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
420 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
421 génération d'une requête infinie.
422 Affiche la liste de candidats que si le user connecté
423 possède un Evaluateur
425 qs
= self
.model
._default_manager
.get_query_set()
426 return qs
.select_related('candidat')
428 class EvaluateurAdmin(VersionAdmin
):
433 ("Offres d'emploi à évaluer", {
434 'fields': ('offres_emploi',)
438 def get_actions(self
, request
):
439 actions
= super(EvaluateurAdmin
, self
).get_actions(request
)
440 del actions
['delete_selected']
443 class AdministrateurRegionalAdmin(VersionAdmin
):
446 class CandidatEvaluationAdmin(VersionAdmin
):
447 list_display
= ('_candidat', '_offre_emploi', 'evaluateur', '_note',
449 readonly_fields
= ('candidat', 'evaluateur')
451 ('Évaluation du candidat', {
452 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
456 def get_actions(self
, request
):
457 actions
= super(CandidatEvaluationAdmin
, self
).get_actions(request
)
458 del actions
['delete_selected']
461 def _note(self
, obj
):
463 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
464 un lien pour Évaluer le candidat.
465 Sinon afficher la note.
468 return "<a href='%s'>Candidat non évalué</a>" % \
469 (reverse('admin:recrutement_candidatevaluation_change',
471 return "<a href='%s'>%s</a>" % \
472 (reverse('admin:recrutement_candidatevaluation_change',
473 args
=(candidat_evaluation
.id,)), obj
.note
)
474 _note
.allow_tags
= True
475 _note
.short_description
= "Note"
476 _note
.admin_order_field
= 'note'
478 def _candidat(self
, obj
):
479 return "<a href='%s'>%s</a>" \
480 % (reverse('admin:recrutement_proxycandidat_change',
481 args
=(obj
.candidat
.id,)), obj
.candidat
)
482 _candidat
.allow_tags
= True
483 _candidat
.short_description
= 'Candidat'
485 def _commentaire(self
, obj
):
487 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
488 dans le champ commentaire, Aucun au lieu de (None)
489 Sinon afficher la note.
491 if obj
.commentaire
is None:
493 return obj
.commentaire
494 _commentaire
.allow_tags
= True
495 _commentaire
.short_description
= "Commentaire"
498 def _offre_emploi(self
, obj
):
499 return "<a href='%s'>%s</a>" % \
500 (reverse('admin:recrutement_proxyoffreemploi_change',
501 args
=(obj
.candidat
.offre_emploi
.id,)), obj
.candidat
.offre_emploi
)
502 _offre_emploi
.allow_tags
= True
503 _offre_emploi
.short_description
= "Voir offre d'emploi"
505 def has_change_permission(self
, request
, obj
=None):
507 Permettre la visualisation dans la changelist
508 mais interdire l'accès à modifier l'objet si l'évaluateur n'est pas
511 return obj
is None or request
.user
== obj
.evaluateur
.user
513 def queryset(self
, request
):
515 Afficher uniquement les évaluations de l'évaluateur, sauf si
516 l'utilisateur est super admin.
518 qs
= self
.model
._default_manager
.get_query_set()
519 user_groupes
= request
.user
.groups
.all()
520 if grp_drh_recrutement
in user_groupes
or \
521 grp_directeurs_bureau_recrutement
in user_groupes
or \
522 grp_administrateurs_recrutement
in user_groupes
or \
523 request
.user
.is_superuser
:
524 return qs
.select_related('offre_emploi')
527 evaluateur
= Evaluateur
.objects
.get(user
=request
.user
)
528 except Evaluateur
.DoesNotExist
:
531 candidats_evaluations
= CandidatEvaluation
.objects
.\
532 filter(evaluateur
=evaluateur
)
533 candidats_evaluations_ids
= [ce
.id for ce
in \
534 candidats_evaluations
.all()]
535 return qs
.select_related('offre_emploi').\
536 filter(id__in
=candidats_evaluations_ids
)
538 class CourrielTemplateAdmin(VersionAdmin
):
539 def get_actions(self
, request
):
540 actions
= super(CourrielTemplateAdmin
, self
).get_actions(request
)
541 del actions
['delete_selected']
544 admin
.site
.register(OffreEmploi
, OffreEmploiAdmin
)
545 admin
.site
.register(ProxyOffreEmploi
, ProxyOffreEmploiAdmin
)
546 admin
.site
.register(Candidat
, CandidatAdmin
)
547 admin
.site
.register(ProxyCandidat
, ProxyCandidatAdmin
)
548 admin
.site
.register(CandidatEvaluation
, CandidatEvaluationAdmin
)
549 admin
.site
.register(Evaluateur
, EvaluateurAdmin
)
550 #admin.site.register(AdministrateurRegional, AdministrateurRegionalAdmin)
551 admin
.site
.register(CourrielTemplate
, CourrielTemplateAdmin
)