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 recrutement
.models
import *
14 from recrutement
.workflow
import grp_administrateurs_recrutement
,\
15 grp_evaluateurs_recrutement
, grp_drh_recrutement
18 class MetaAdmin(VersionAdmin):
19 def get_actions(self, request):
23 class OffreEmploiAdmin(VersionAdmin
):
24 date_hierarchy
= 'date_creation'
25 list_display
= ('nom', 'resume', 'date_limite', 'region', 'statut',
26 'est_affiche', '_candidatsList')
27 list_filter
= ('statut', 'est_affiche', )
28 actions
= ['affecter_evaluateurs_offre_emploi', ]
30 def get_actions(self
, request
):
31 actions
= super(OffreEmploiAdmin
, self
).get_actions(request
)
32 del actions
['delete_selected']
35 # Affecter un évaluateurs à des offres d'emploi
36 def affecter_evaluateurs_offre_emploi(modeladmin
, obj
, candidats
):
37 selected
= obj
.POST
.getlist(admin
.ACTION_CHECKBOX_NAME
)
39 return HttpResponseRedirect(reverse('affecter_evaluateurs_offre_emploi')+
40 "?ids=%s" % (",".join(selected
)))
41 affecter_evaluateurs_offre_emploi
.short_description
= u
'Affecter évaluateur(s)'
43 # Afficher la liste des candidats pour l'offre d'emploi
44 def _candidatsList(self
, obj
):
45 return "<a href='%s?offre_emploi__id__exact=%s'>Voir les candidats \
46 </a>" % (reverse('admin:recrutement_candidat_changelist'), obj
.id)
47 _candidatsList
.allow_tags
= True
48 _candidatsList
.short_description
= "Afficher la liste des candidats"
51 def queryset(self
, request
):
52 qs
= self
.model
._default_manager
.get_query_set()
53 # Si user est superuser afficher toutes les offres d'emploi
54 user_groupes
= request
.user
.groups
.all()
55 if not grp_drh_recrutement
in user_groupes
and \
56 not request
.user
.is_superuser
:
58 Si le user n'est ni un évaluateur ni un administrateur régional,
62 if grp_evaluateurs_recrutement
in user_groupes
:
64 user
= Evaluateur
.objects
.get(user
=request
.user
)
65 except Evaluateur
.DoesNotExist
:
67 elif grp_administrateurs_recrutement
in user_groupes
:
69 user
= AdministrateurRegional
.objects
.get(user
=request
.user
)
70 except AdministrateurRegional
.DoesNotExist
:
75 if type(user
) is AdministrateurRegional
:
76 region_ids
= [g
.id for g
in user
.regions
.all()]
77 return qs
.select_related('offre_emploi').\
78 filter(region__in
=region_ids
)
79 if type(user
) is Evaluateur
:
80 candidats
= [g
for g
in user
.candidats
.all()]
81 offre_emploi_ids
= [c
.offre_emploi
.id for c
in candidats
]
82 return qs
.select_related('offre_emploi').\
83 filter(id__in
=offre_emploi_ids
)
85 return qs
.select_related('offre_emploi')
87 def has_change_permission(self
, request
, obj
=None):
88 user_groupes
= request
.user
.groups
.all()
89 if grp_drh_recrutement
in user_groupes
or \
90 grp_administrateurs_recrutement
in user_groupes
or \
91 request
.user
.is_superuser
:
95 class ProxyOffreEmploiAdmin(OffreEmploiAdmin
):
96 list_display
= ('nom', 'resume', 'date_limite', 'region', 'statut',
98 readonly_fields
= ('description', 'bureau',
99 'duree_affectation', 'renumeration',
100 'debut_affectation', 'lieu_affectation', 'nom',
101 'resume', 'date_limite', 'region')
106 ('Description générale', {
107 'fields': ('resume','description', 'date_limite', )
110 'fields': ('lieu_affectation', 'bureau', 'region', )
113 'fields': ('debut_affectation', 'duree_affectation',
118 def response_change(self
, request
, obj
):
119 response
= super(ProxyOffreEmploiAdmin
, self
).response_change(request
, obj
)
120 user_groupes
= request
.user
.groups
.all()
121 if grp_drh_recrutement
in user_groupes
or \
122 request
.user
.is_superuser
:
123 return HttpResponseRedirect(reverse('admin:recrutement_offreemploi_changelist'))
124 return HttpResponseRedirect(reverse('admin:recrutement_proxyoffreemploi_changelist'))
126 def has_add_permission(self
, request
):
129 def has_delete_permission(self
, request
, obj
=None):
132 def has_change_permission(self
, request
, obj
=None):
133 user_groupes
= request
.user
.groups
.all()
134 if grp_evaluateurs_recrutement
in user_groupes
or \
135 grp_drh_recrutement
in user_groupes
or \
136 request
.user
.is_superuser
:
140 """class ProxyEvaluateur(Evaluateur.candidats.through):
142 Ce proxy sert uniquement dans l'admin à disposer d'un libellé
147 verbose_name = "évaluateur"
149 class CandidatPieceInline(admin
.TabularInline
):
150 model
= CandidatPiece
151 fields
= ('candidat', 'nom', 'path',)
155 class CandidatEvaluationInlineFormSet(BaseInlineFormSet
):
157 Empêche la suppression d'une évaluation pour le CandidatEvaluationInline
159 def __init__(self
, *args
, **kwargs
):
160 super(CandidatEvaluationInlineFormSet
, self
).__init__(*args
, **kwargs
)
161 self
.can_delete
= False
163 class CandidatEvaluationInline(admin
.TabularInline
):
164 model
= CandidatEvaluation
165 fields
= ('evaluateur', 'note', 'commentaire')
168 formset
= CandidatEvaluationInlineFormSet
170 def get_readonly_fields(self
, request
, obj
=None):
172 Empêche la modification des évaluations
175 return self
.readonly_fields
+('evaluateur', 'note', 'commentaire')
176 return self
.readonly_fields
179 class CandidatAdmin(VersionAdmin
):
180 date_hierarchy
= 'date_creation'
181 list_display
= ('nom', 'prenom', 'offre_emploi','statut',
182 'voir_offre_emploi', 'calculer_moyenne',
183 'afficher_candidat',)
184 list_filter
= ('offre_emploi', )
187 'fields': ('offre_emploi', )
189 ('Informations personnelles', {
190 'fields': ('prenom','nom','genre', 'nationalite',
191 'situation_famille', 'nombre_dependant',)
194 'fields': ('telephone', 'email', 'adresse', 'ville',
195 'etat_province', 'code_postal', 'pays', )
197 ('Informations professionnelles', {
198 'fields': ('niveau_diplome','employeur_actuel',
199 'poste_actuel', 'domaine_professionnel',)
202 'fields': ('statut', )
207 CandidatEvaluationInline
,
210 actions
= ['envoyer_courriel_candidats']
212 def get_actions(self
, request
):
213 actions
= super(CandidatAdmin
, self
).get_actions(request
)
214 del actions
['delete_selected']
217 # Envoyer un courriel à des candidats
218 def envoyer_courriel_candidats(modeladmin
, obj
, candidats
):
219 selected
= obj
.POST
.getlist(admin
.ACTION_CHECKBOX_NAME
)
221 return HttpResponseRedirect(reverse('selectionner_template')+
222 "?ids=%s" % (",".join(selected
)))
223 envoyer_courriel_candidats
.short_description
= u
'Envoyer courriel'
225 # Évaluer un candidat
226 def evaluer_candidat(self
, obj
):
227 return "<a href='%s?candidat__id__exact=%s'>Évaluer le candidat</a>" % \
228 (reverse('admin:recrutement_candidatevaluation_changelist'),
230 evaluer_candidat
.allow_tags
= True
231 evaluer_candidat
.short_description
= 'Évaluation'
233 # Afficher un candidat
234 def afficher_candidat(self
, obj
):
235 return "<a href='%s'>Voir le candidat</a>" % \
236 (reverse('admin:recrutement_proxycandidat_change', args
=(obj
.id,)))
237 afficher_candidat
.allow_tags
= True
238 afficher_candidat
.short_description
= u
'Détails du candidat'
240 # Voir l'offre d'emploi
241 def voir_offre_emploi(self
, obj
):
242 return "<a href='%s'>Voir l'offre d'emploi</a>" % \
243 (reverse('admin:recrutement_proxyoffreemploi_change',
244 args
=(obj
.offre_emploi
.id,)))
245 voir_offre_emploi
.allow_tags
= True
246 voir_offre_emploi
.short_description
= "Afficher l'offre d'emploi"
248 # Calculer la moyenne des notes
249 def calculer_moyenne(self
, obj
):
250 evaluations
= CandidatEvaluation
.objects
.filter(candidat
=obj
)
251 offre_emploi
= obj
.offre_emploi
253 notes
= [evaluation
.note
for evaluation
in evaluations
.all() \
254 if evaluation
.note
is not None]
256 if len(notes
) > 0 and offre_emploi
.date_limite
<= datetime
.date
.today():
257 moyenne_votes
= float(sum(notes
)) / len(notes
)
259 moyenne_votes
= "Non disponible"
261 calculer_moyenne
.allow_tags
= True
262 calculer_moyenne
.short_description
= "Moyenne des notes"
264 def add_delete_permission(self
, request
, obj
=None) :
265 user_groupes
= request
.user
.groups
.all()
266 if grp_drh_recrutement
in user_groupes
or \
267 grp_administrateurs_recrutement
in user_groupes
or \
268 request
.user
.is_superuser
:
272 def has_add_permission(self
, request
):
273 return self
.add_delete_permission(request
, request
)
275 def has_delete_permission(self
, request
, obj
=None):
276 return self
.add_delete_permission(request
, request
)
278 def has_change_permission(self
, request
, obj
=None):
279 user_groupes
= request
.user
.groups
.all()
280 if grp_drh_recrutement
in user_groupes
or \
281 grp_administrateurs_recrutement
in user_groupes
or \
282 request
.user
.is_superuser
:
286 def queryset(self
, request
):
288 Spécifie un queryset limité, autrement Django exécute un
289 select_related() sans paramètre, ce qui a pour effet de charger tous
290 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
291 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
292 génération d'une requête infinie.
295 qs
= self
.model
._default_manager
.get_query_set()
296 # Si user est superuser afficher tous les candidats
297 user_groupes
= request
.user
.groups
.all()
298 if not grp_drh_recrutement
in user_groupes
and \
299 not request
.user
.is_superuser
:
300 # Si le user n'est ni un évaluateur ni un administrateur régional,
304 if grp_evaluateurs_recrutement
in user_groupes
:
306 user
= Evaluateur
.objects
.get(user
=request
.user
)
307 except Evaluateur
.DoesNotExist
:
310 elif grp_administrateurs_recrutement in user_groupes:
312 user = AdministrateurRegional.objects.get(user=obj.user)
313 except AdministrateurRegional.DoesNotExist:
318 ids
= [c
.id for c
in user
.candidats
.all()]
319 return qs
.select_related('candidats').filter(id__in
=ids
)
320 return qs
.select_related('candidats')
322 class ProxyCandidatAdmin(CandidatAdmin
):
323 readonly_fields
= ('statut', 'offre_emploi', 'prenom', 'nom',
324 'genre', 'nationalite', 'situation_famille',
325 'nombre_dependant', 'telephone', 'email', 'adresse',
326 'ville', 'etat_province', 'code_postal', 'pays',
327 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
328 'domaine_professionnel', 'pieces_jointes',)
331 'fields': ('offre_emploi', )
333 ('Informations personnelles', {
334 'fields': ('prenom','nom','genre', 'nationalite',
335 'situation_famille', 'nombre_dependant',)
338 'fields': ('telephone', 'email', 'adresse', 'ville',
339 'etat_province', 'code_postal', 'pays', )
341 ('Informations professionnelles', {
342 'fields': ('niveau_diplome','employeur_actuel',
343 'poste_actuel', 'domaine_professionnel',)
348 def response_change(self
, request
, obj
):
349 response
= super(ProxyCandidatAdmin
, self
).response_change(request
, obj
)
350 user_groupes
= request
.user
.groups
.all()
351 if grp_drh_recrutement
in user_groupes
or \
352 request
.user
.is_superuser
:
353 return HttpResponseRedirect(reverse('admin:recrutement_candidat_changelist'))
354 return HttpResponseRedirect(reverse('admin:recrutement_proxycandidat_changelist'))
356 def has_add_permission(self
, request
):
359 def has_delete_permission(self
, request
, obj
=None):
362 def has_change_permission(self
, request
, obj
=None):
363 user_groupes
= request
.user
.groups
.all()
364 if grp_drh_recrutement
in user_groupes
or \
365 grp_administrateurs_recrutement
in user_groupes
or \
366 grp_evaluateurs_recrutement
in user_groupes
or \
367 request
.user
.is_superuser
:
371 class CandidatPieceAdmin(admin
.ModelAdmin
):
372 list_display
= ('nom', 'candidat', )
374 def queryset(self
, request
):
376 Spécifie un queryset limité, autrement Django exécute un
377 select_related() sans paramètre, ce qui a pour effet de charger tous
378 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
379 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
380 génération d'une requête infinie.
381 Affiche la liste de candidats que si le user connecté
382 possède un Evaluateur
384 qs
= self
.model
._default_manager
.get_query_set()
385 return qs
.select_related('candidat')
387 class EvaluateurAdmin(VersionAdmin
):
392 ("Offres d'emploi à évaluer", {
393 'fields': ('offres_emploi',)
397 def get_actions(self
, request
):
398 actions
= super(EvaluateurAdmin
, self
).get_actions(request
)
399 del actions
['delete_selected']
402 class AdministrateurRegionalAdmin(VersionAdmin
):
405 class CandidatEvaluationAdmin(VersionAdmin
):
406 list_display
= ('_candidat', '_offre_emploi', 'evaluateur', '_note',
408 readonly_fields
= ('candidat', 'evaluateur')
410 ('Évaluation du candidat', {
411 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
415 def get_actions(self
, request
):
416 actions
= super(CandidatEvaluationAdmin
, self
).get_actions(request
)
417 del actions
['delete_selected']
420 def _note(self
, obj
):
422 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
423 un lien pour Évaluer le candidat.
424 Sinon afficher la note.
426 evaluateur
= obj
.evaluateur
427 candidat
= obj
.candidat
428 candidat_evaluation
= CandidatEvaluation
.objects
.\
429 get(candidat
=candidat
, evaluateur
=evaluateur
)
431 return "<a href='%s'>Candidat non évalué</a>" % \
432 (reverse('admin:recrutement_candidatevaluation_change',
433 args
=(candidat_evaluation
.id,)))
434 return "<a href='%s'>%s</a>" % \
435 (reverse('admin:recrutement_candidatevaluation_change',
436 args
=(candidat_evaluation
.id,)), obj
.note
)
438 _note
.allow_tags
= True
439 _note
.short_description
= "Votre note"
440 _note
.admin_order_field
= 'note'
442 def _candidat(self
, obj
):
443 return "<a href='%s'>%s</a>" \
444 % (reverse('admin:recrutement_proxycandidat_change',
445 args
=(obj
.candidat
.id,)), obj
.candidat
)
446 _candidat
.allow_tags
= True
447 _candidat
.short_description
= 'Candidat'
449 def _commentaire(self
, obj
):
451 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
452 dans le champ commentaire, Aucun au lieu de (None)
453 Sinon afficher la note.
455 if obj
.commentaire
is None:
457 return obj
.commentaire
458 _commentaire
.allow_tags
= True
459 _commentaire
.short_description
= "Commentaire"
462 def _offre_emploi(self
, obj
):
463 return "<a href='%s'>%s</a>" % \
464 (reverse('admin:recrutement_proxyoffreemploi_change',
465 args
=(obj
.candidat
.offre_emploi
.id,)), obj
.candidat
.offre_emploi
)
466 _offre_emploi
.allow_tags
= True
467 _offre_emploi
.short_description
= "Voir offre d'emploi"
469 def has_change_permission(self
, request
, obj
=None):
471 Permettre la visualisation dans la changelist
472 mais interdire l'accès à modifier l'objet si l'évaluateur n'est pas
475 return obj
is None or request
.user
== obj
.evaluateur
.user
477 def queryset(self
, request
):
479 Afficher uniquement les évaluations de l'évaluateur, sauf si
480 l'utilisateur est super admin.
482 qs
= self
.model
._default_manager
.get_query_set()
483 user_groupes
= request
.user
.groups
.all()
484 if grp_drh_recrutement
in user_groupes
or \
485 request
.user
.is_superuser
:
486 return qs
.select_related('offre_emploi')
489 evaluateur
= Evaluateur
.objects
.get(user
=request
.user
)
490 except Evaluateur
.DoesNotExist
:
493 candidats_evaluations
= CandidatEvaluation
.objects
.\
494 filter(evaluateur
=evaluateur
)
495 candidats_evaluations_ids
= [ce
.id for ce
in \
496 candidats_evaluations
.all()]
497 return qs
.select_related('offre_emploi').\
498 filter(id__in
=candidats_evaluations_ids
)
500 class CourrielTemplateAdmin(VersionAdmin
):
501 def get_actions(self
, request
):
502 actions
= super(CourrielTemplateAdmin
, self
).get_actions(request
)
503 del actions
['delete_selected']
506 admin
.site
.register(OffreEmploi
, OffreEmploiAdmin
)
507 admin
.site
.register(ProxyOffreEmploi
, ProxyOffreEmploiAdmin
)
508 admin
.site
.register(Candidat
, CandidatAdmin
)
509 admin
.site
.register(ProxyCandidat
, ProxyCandidatAdmin
)
510 admin
.site
.register(CandidatEvaluation
, CandidatEvaluationAdmin
)
511 admin
.site
.register(Evaluateur
, EvaluateurAdmin
)
512 #admin.site.register(AdministrateurRegional, AdministrateurRegionalAdmin)
513 admin
.site
.register(CourrielTemplate
, CourrielTemplateAdmin
)