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
9 from reversion
.admin
import VersionAdmin
10 from datamaster_modeles
.models
import Employe
, Implantation
, Region
11 from django
.forms
.models
import BaseInlineFormSet
13 from project
.rh
import models
as rh
14 from recrutement
.models
import *
15 from recrutement
.workflow
import grp_administrateurs_recrutement
,\
16 grp_evaluateurs_recrutement
, grp_drh_recrutement
17 from recrutement
.forms
import *
19 class ProxyEvaluateur(Evaluateur
.offres_emploi
.through
):
21 Ce proxy sert uniquement dans l'admin à disposer d'un libellé
26 verbose_name
= "évaluateur"
28 class EvaluateurInline(admin
.TabularInline
):
29 model
= ProxyEvaluateur
30 fields
= ('evaluateur',)
33 class OffreEmploiAdmin(VersionAdmin
):
34 date_hierarchy
= 'date_creation'
35 list_display
= ('nom', 'date_limite', 'region', 'statut',
36 'est_affiche', '_candidatsList', )
37 exclude
= ('actif', 'poste_nom', 'resume',)
38 list_filter
= ('statut',)
39 actions
= ['affecter_evaluateurs_offre_emploi', ]
40 form
= OffreEmploiForm
41 inlines
= [EvaluateurInline
, ]
43 def get_actions(self
, request
):
44 actions
= super(OffreEmploiAdmin
, self
).get_actions(request
)
45 del actions
['delete_selected']
48 # Affecter un évaluateurs à des offres d'emploi
49 def affecter_evaluateurs_offre_emploi(modeladmin
, obj
, candidats
):
50 selected
= obj
.POST
.getlist(admin
.ACTION_CHECKBOX_NAME
)
52 return HttpResponseRedirect(reverse('affecter_evaluateurs_offre_emploi')+
53 "?ids=%s" % (",".join(selected
)))
54 affecter_evaluateurs_offre_emploi
.short_description
= u
'Affecter évaluateur(s)'
56 # Afficher la liste des candidats pour l'offre d'emploi
57 def _candidatsList(self
, obj
):
58 return "<a href='%s?offre_emploi__id__exact=%s'>Voir les candidats \
59 </a>" % (reverse('admin:recrutement_candidat_changelist'), obj
.id)
60 _candidatsList
.allow_tags
= True
61 _candidatsList
.short_description
= "Afficher la liste des candidats"
63 def queryset(self
, request
):
64 qs
= self
.model
._default_manager
.get_query_set()
65 # Si user est superuser afficher toutes les offres d'emploi
66 user_groupes
= request
.user
.groups
.all()
67 if not grp_drh_recrutement
in user_groupes
and \
68 not request
.user
.is_superuser
:
70 Si le user n'est ni un évaluateur ni un administrateur régional,
74 if grp_evaluateurs_recrutement
in user_groupes
:
76 user
= Evaluateur
.objects
.get(user
=request
.user
)
77 except Evaluateur
.DoesNotExist
:
79 elif grp_administrateurs_recrutement
in user_groupes
:
81 user
= AdministrateurRegional
.objects
.get(user
=request
.user
)
82 except AdministrateurRegional
.DoesNotExist
:
87 if type(user
) is AdministrateurRegional
:
88 region_ids
= [g
.id for g
in user
.regions
.all()]
89 return qs
.select_related('offre_emploi').\
90 filter(region__in
=region_ids
)
91 if type(user
) is Evaluateur
:
92 candidats
= [g
for g
in user
.candidats
.all()]
93 offre_emploi_ids
= [c
.offre_emploi
.id for c
in candidats
]
94 return qs
.select_related('offre_emploi').\
95 filter(id__in
=offre_emploi_ids
)
97 return qs
.select_related('offre_emploi')
99 def has_change_permission(self
, request
, obj
=None):
100 user_groupes
= request
.user
.groups
.all()
101 if grp_drh_recrutement
in user_groupes
or \
102 grp_administrateurs_recrutement
in user_groupes
or \
103 request
.user
.is_superuser
:
107 class ProxyOffreEmploiAdmin(OffreEmploiAdmin
):
108 list_display
= ('nom', 'resume', 'date_limite', 'region', 'statut',
110 readonly_fields
= ('description', 'bureau', 'duree_affectation',
111 'renumeration', 'debut_affectation', 'lieu_affectation',
112 'nom', 'resume', 'date_limite', 'region')
117 ('Description générale', {
118 'fields': ('resume','description', 'date_limite', )
121 'fields': ('lieu_affectation', 'bureau', 'region', )
124 'fields': ('debut_affectation', 'duree_affectation',
129 def get_actions(self
, request
):
130 actions
= super(ProxyOffreEmploiAdmin
, self
).get_actions(request
)
131 del actions
['affecter_evaluateurs_offre_emploi']
134 def response_change(self
, request
, obj
):
135 response
= super(ProxyOffreEmploiAdmin
, self
).response_change(request
,
137 user_groupes
= request
.user
.groups
.all()
138 if grp_drh_recrutement
in user_groupes
or \
139 request
.user
.is_superuser
:
140 return HttpResponseRedirect(reverse\
141 ('admin:recrutement_offreemploi_changelist'))
142 return HttpResponseRedirect(reverse\
143 ('admin:recrutement_proxyoffreemploi_changelist'))
145 def has_add_permission(self
, request
):
148 def has_delete_permission(self
, request
, obj
=None):
151 def has_change_permission(self
, request
, obj
=None):
152 user_groupes
= request
.user
.groups
.all()
153 if grp_evaluateurs_recrutement
in user_groupes
or \
154 grp_drh_recrutement
in user_groupes
or \
155 request
.user
.is_superuser
:
159 class CandidatPieceInline(admin
.TabularInline
):
160 model
= CandidatPiece
161 fields
= ('candidat', 'nom', 'path',)
165 class CandidatEvaluationInlineFormSet(BaseInlineFormSet
):
167 Empêche la suppression d'une évaluation pour le CandidatEvaluationInline
169 def __init__(self
, *args
, **kwargs
):
170 super(CandidatEvaluationInlineFormSet
, self
).__init__(*args
, **kwargs
)
171 self
.can_delete
= False
173 class CandidatEvaluationInline(admin
.TabularInline
):
174 model
= CandidatEvaluation
175 fields
= ('evaluateur', 'note', 'commentaire')
178 formset
= CandidatEvaluationInlineFormSet
180 def get_readonly_fields(self
, request
, obj
=None):
182 Empêche la modification des évaluations
185 return self
.readonly_fields
+('evaluateur', 'note', 'commentaire')
186 return self
.readonly_fields
188 class CandidatAdmin(VersionAdmin
):
189 exclude
= ('actif', )
190 date_hierarchy
= 'date_creation'
191 list_display
= ('nom', 'prenom', 'offre_emploi','statut',
192 'voir_offre_emploi', 'calculer_moyenne',
193 'afficher_candidat',)
194 list_filter
= ('offre_emploi', )
198 'fields': ('offre_emploi', )
200 ('Informations personnelles', {
201 'fields': ('prenom','nom','genre', 'nationalite',
202 'situation_famille', 'nombre_dependant',)
205 'fields': ('telephone', 'email', 'adresse', 'ville',
206 'etat_province', 'code_postal', 'pays', )
208 ('Informations professionnelles', {
209 'fields': ('niveau_diplome','employeur_actuel',
210 'poste_actuel', 'domaine_professionnel',)
213 'fields': ('statut', )
218 CandidatEvaluationInline
,
221 actions
= ['envoyer_courriel_candidats']
223 def get_actions(self
, request
):
224 actions
= super(CandidatAdmin
, self
).get_actions(request
)
225 del actions
['delete_selected']
228 # Envoyer un courriel à des candidats
229 def envoyer_courriel_candidats(modeladmin
, obj
, candidats
):
230 selected
= obj
.POST
.getlist(admin
.ACTION_CHECKBOX_NAME
)
232 return HttpResponseRedirect(reverse('selectionner_template')+
233 "?ids=%s" % (",".join(selected
)))
234 envoyer_courriel_candidats
.short_description
= u
'Envoyer courriel'
236 # Évaluer un candidat
237 def evaluer_candidat(self
, obj
):
238 return "<a href='%s?candidat__id__exact=%s'>Évaluer le candidat</a>" % \
239 (reverse('admin:recrutement_candidatevaluation_changelist'),
241 evaluer_candidat
.allow_tags
= True
242 evaluer_candidat
.short_description
= 'Évaluation'
244 # Afficher un candidat
245 def afficher_candidat(self
, obj
):
246 return "<a href='%s'>Voir le candidat</a>" % \
247 (reverse('admin:recrutement_proxycandidat_change', args
=(obj
.id,)))
248 afficher_candidat
.allow_tags
= True
249 afficher_candidat
.short_description
= u
'Détails du candidat'
251 # Voir l'offre d'emploi
252 def voir_offre_emploi(self
, obj
):
253 return "<a href='%s'>Voir l'offre d'emploi</a>" % \
254 (reverse('admin:recrutement_proxyoffreemploi_change',
255 args
=(obj
.offre_emploi
.id,)))
256 voir_offre_emploi
.allow_tags
= True
257 voir_offre_emploi
.short_description
= "Afficher l'offre d'emploi"
259 # Calculer la moyenne des notes
260 def calculer_moyenne(self
, obj
):
261 evaluations
= CandidatEvaluation
.objects
.filter(candidat
=obj
)
262 offre_emploi
= obj
.offre_emploi
264 notes
= [evaluation
.note
for evaluation
in evaluations
.all() \
265 if evaluation
.note
is not None]
268 moyenne_votes
= float(sum(notes
)) / len(notes
)
270 moyenne_votes
= "Non disponible"
272 calculer_moyenne
.allow_tags
= True
273 calculer_moyenne
.short_description
= "Moyenne des notes"
275 def add_delete_permission(self
, request
, obj
=None) :
276 user_groupes
= request
.user
.groups
.all()
277 if grp_drh_recrutement
in user_groupes
or \
278 grp_administrateurs_recrutement
in user_groupes
or \
279 request
.user
.is_superuser
:
283 def has_add_permission(self
, request
):
284 return self
.add_delete_permission(request
, request
)
286 def has_delete_permission(self
, request
, obj
=None):
287 return self
.add_delete_permission(request
, request
)
289 def has_change_permission(self
, request
, obj
=None):
290 user_groupes
= request
.user
.groups
.all()
291 if grp_drh_recrutement
in user_groupes
or \
292 grp_administrateurs_recrutement
in user_groupes
or \
293 request
.user
.is_superuser
:
297 def queryset(self
, request
):
299 Spécifie un queryset limité, autrement Django exécute un
300 select_related() sans paramètre, ce qui a pour effet de charger tous
301 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
302 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
303 génération d'une requête infinie.
306 qs
= self
.model
._default_manager
.get_query_set()
307 # Si user est superuser afficher tous les candidats
308 user_groupes
= request
.user
.groups
.all()
309 if not grp_drh_recrutement
in user_groupes
and \
310 not request
.user
.is_superuser
:
311 # Si le user n'est ni un évaluateur ni un administrateur régional,
315 if grp_evaluateurs_recrutement
in user_groupes
:
317 user
= Evaluateur
.objects
.get(user
=request
.user
)
318 except Evaluateur
.DoesNotExist
:
321 elif grp_administrateurs_recrutement in user_groupes:
323 user = AdministrateurRegional.objects.get(user=obj.user)
324 except AdministrateurRegional.DoesNotExist:
329 ids
= [c
.id for c
in user
.candidats
.all()]
330 return qs
.select_related('candidats').filter(id__in
=ids
)
331 return qs
.select_related('candidats')
333 class ProxyCandidatAdmin(CandidatAdmin
):
334 readonly_fields
= ('statut', 'offre_emploi', 'prenom', 'nom',
335 'genre', 'nationalite', 'situation_famille',
336 'nombre_dependant', 'telephone', 'email', 'adresse',
337 'ville', 'etat_province', 'code_postal', 'pays',
338 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
339 'domaine_professionnel', 'pieces_jointes',)
342 'fields': ('offre_emploi', )
344 ('Informations personnelles', {
345 'fields': ('prenom','nom','genre', 'nationalite',
346 'situation_famille', 'nombre_dependant',)
349 'fields': ('telephone', 'email', 'adresse', 'ville',
350 'etat_province', 'code_postal', 'pays', )
352 ('Informations professionnelles', {
353 'fields': ('niveau_diplome','employeur_actuel',
354 'poste_actuel', 'domaine_professionnel',)
359 def response_change(self
, request
, obj
):
360 response
= super(ProxyCandidatAdmin
, self
).response_change(request
, obj
)
361 user_groupes
= request
.user
.groups
.all()
362 if grp_drh_recrutement
in user_groupes
or \
363 request
.user
.is_superuser
:
364 return HttpResponseRedirect(reverse\
365 ('admin:recrutement_candidat_changelist'))
366 return HttpResponseRedirect(reverse\
367 ('admin:recrutement_proxycandidat_changelist'))
369 def has_add_permission(self
, request
):
372 def has_delete_permission(self
, request
, obj
=None):
375 def has_change_permission(self
, request
, obj
=None):
376 user_groupes
= request
.user
.groups
.all()
377 if grp_drh_recrutement
in user_groupes
or \
378 grp_administrateurs_recrutement
in user_groupes
or \
379 grp_evaluateurs_recrutement
in user_groupes
or \
380 request
.user
.is_superuser
:
384 class CandidatPieceAdmin(admin
.ModelAdmin
):
385 list_display
= ('nom', 'candidat', )
387 def queryset(self
, request
):
389 Spécifie un queryset limité, autrement Django exécute un
390 select_related() sans paramètre, ce qui a pour effet de charger tous
391 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
392 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
393 génération d'une requête infinie.
394 Affiche la liste de candidats que si le user connecté
395 possède un Evaluateur
397 qs
= self
.model
._default_manager
.get_query_set()
398 return qs
.select_related('candidat')
400 class EvaluateurAdmin(VersionAdmin
):
405 ("Offres d'emploi à évaluer", {
406 'fields': ('offres_emploi',)
410 def get_actions(self
, request
):
411 actions
= super(EvaluateurAdmin
, self
).get_actions(request
)
412 del actions
['delete_selected']
415 class AdministrateurRegionalAdmin(VersionAdmin
):
418 class CandidatEvaluationAdmin(VersionAdmin
):
419 list_display
= ('_candidat', '_offre_emploi', 'evaluateur', '_note',
421 readonly_fields
= ('candidat', 'evaluateur')
423 ('Évaluation du candidat', {
424 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
428 def get_actions(self
, request
):
429 actions
= super(CandidatEvaluationAdmin
, self
).get_actions(request
)
430 del actions
['delete_selected']
433 def _note(self
, obj
):
435 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
436 un lien pour Évaluer le candidat.
437 Sinon afficher la note.
439 #evaluateur = obj.evaluateur
440 #candidat = obj.candidat
441 #candidat_evaluation = CandidatEvaluation.objects.get(candidat=candidat, evaluateur=evaluateur)
442 #import pdb;pdb.set_trace()
444 return "<a href='%s'>Candidat non évalué</a>" % \
445 (reverse('admin:recrutement_candidatevaluation_change',
447 return "<a href='%s'>%s</a>" % \
448 (reverse('admin:recrutement_candidatevaluation_change',
449 args
=(candidat_evaluation
.id,)), obj
.note
)
450 _note
.allow_tags
= True
451 _note
.short_description
= "Note"
452 _note
.admin_order_field
= 'note'
454 def _candidat(self
, obj
):
455 return "<a href='%s'>%s</a>" \
456 % (reverse('admin:recrutement_proxycandidat_change',
457 args
=(obj
.candidat
.id,)), obj
.candidat
)
458 _candidat
.allow_tags
= True
459 _candidat
.short_description
= 'Candidat'
461 def _commentaire(self
, obj
):
463 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
464 dans le champ commentaire, Aucun au lieu de (None)
465 Sinon afficher la note.
467 if obj
.commentaire
is None:
469 return obj
.commentaire
470 _commentaire
.allow_tags
= True
471 _commentaire
.short_description
= "Commentaire"
474 def _offre_emploi(self
, obj
):
475 return "<a href='%s'>%s</a>" % \
476 (reverse('admin:recrutement_proxyoffreemploi_change',
477 args
=(obj
.candidat
.offre_emploi
.id,)), obj
.candidat
.offre_emploi
)
478 _offre_emploi
.allow_tags
= True
479 _offre_emploi
.short_description
= "Voir offre d'emploi"
481 def has_change_permission(self
, request
, obj
=None):
483 Permettre la visualisation dans la changelist
484 mais interdire l'accès à modifier l'objet si l'évaluateur n'est pas
487 return obj
is None or request
.user
== obj
.evaluateur
.user
489 def queryset(self
, request
):
491 Afficher uniquement les évaluations de l'évaluateur, sauf si
492 l'utilisateur est super admin.
494 qs
= self
.model
._default_manager
.get_query_set()
495 user_groupes
= request
.user
.groups
.all()
496 if grp_drh_recrutement
in user_groupes
or \
497 request
.user
.is_superuser
:
498 return qs
.select_related('offre_emploi')
501 evaluateur
= Evaluateur
.objects
.get(user
=request
.user
)
502 except Evaluateur
.DoesNotExist
:
505 candidats_evaluations
= CandidatEvaluation
.objects
.\
506 filter(evaluateur
=evaluateur
)
507 candidats_evaluations_ids
= [ce
.id for ce
in \
508 candidats_evaluations
.all()]
509 return qs
.select_related('offre_emploi').\
510 filter(id__in
=candidats_evaluations_ids
)
512 class CourrielTemplateAdmin(VersionAdmin
):
513 def get_actions(self
, request
):
514 actions
= super(CourrielTemplateAdmin
, self
).get_actions(request
)
515 del actions
['delete_selected']
518 admin
.site
.register(OffreEmploi
, OffreEmploiAdmin
)
519 admin
.site
.register(ProxyOffreEmploi
, ProxyOffreEmploiAdmin
)
520 admin
.site
.register(Candidat
, CandidatAdmin
)
521 admin
.site
.register(ProxyCandidat
, ProxyCandidatAdmin
)
522 admin
.site
.register(CandidatEvaluation
, CandidatEvaluationAdmin
)
523 admin
.site
.register(Evaluateur
, EvaluateurAdmin
)
524 #admin.site.register(AdministrateurRegional, AdministrateurRegionalAdmin)
525 admin
.site
.register(CourrielTemplate
, CourrielTemplateAdmin
)