c7fc9b610cc9016e70fe240f1d10826301124f51
[auf_rh_dae.git] / project / recrutement / admin.py
1 # -*- encoding: utf-8 -*-
2
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
8 from reversion.admin import VersionAdmin
9 from datamaster_modeles.models import Employe, Implantation, Region
10
11 from recrutement.models import *
12 from recrutement.workflow import grp_administrateurs_recrutement,\
13 grp_evaluateurs_recrutement, grp_drh_recrutement
14
15 class OffreEmploiAdmin(VersionAdmin):
16 date_hierarchy = 'date_creation'
17 list_display = ('nom', 'resume', 'date_limite', 'region', 'statut',
18 'est_affiche', '_candidatsList')
19 list_filter = ('statut', 'est_affiche', )
20 actions = ['affecter_evaluateurs_offre_emploi', ]
21 # Affecter un évaluateurs à des offres d'emploi
22 def affecter_evaluateurs_offre_emploi(modeladmin, obj, candidats):
23 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
24
25 return HttpResponseRedirect(reverse('affecter_evaluateurs_offre_emploi')+
26 "?ids=%s" % (",".join(selected)))
27 affecter_evaluateurs_offre_emploi.short_description = u'Affecter évaluateur(s)'
28
29 # Afficher la liste des candidats pour l'offre d'emploi
30 def _candidatsList(self, obj):
31 return "<a href='%s?offre_emploi__id__exact=%s'>Voir les candidats \
32 </a>" % (reverse('admin:recrutement_candidat_changelist'), obj.id)
33 _candidatsList.allow_tags = True
34 _candidatsList.short_description = "Afficher la liste des candidats"
35
36
37 def queryset(self, request):
38 qs = self.model._default_manager.get_query_set()
39 # Si user est superuser afficher toutes les offres d'emploi
40 user_groupes = request.user.groups.all()
41 if not grp_drh_recrutement in user_groupes:
42 """
43 Si le user n'est ni un évaluateur ni un administrateur régional,
44 retourner none
45 Vérifier groupes
46 """
47 if grp_evaluateurs_recrutement in user_groupes:
48 try:
49 user = Evaluateur.objects.get(user=request.user)
50 except Evaluateur.DoesNotExist:
51 return qs.none()
52 elif grp_administrateurs_recrutement in user_groupes:
53 try:
54 user = AdministrateurRegional.objects.get(user=request.user)
55 except AdministrateurRegional.DoesNotExist:
56 return qs.none()
57 else:
58 return qs.none()
59
60 if type(user) is AdministrateurRegional:
61 region_ids = [g.id for g in user.regions.all()]
62 return qs.select_related('offre_emploi').\
63 filter(region__in=region_ids)
64 if type(user) is Evaluateur:
65 candidats = [g for g in user.candidats.all()]
66 offre_emploi_ids = [c.offre_emploi.id for c in candidats]
67 return qs.select_related('offre_emploi').\
68 filter(id__in=offre_emploi_ids)
69 return qs.none()
70 return qs.select_related('offre_emploi')
71
72 def has_change_permission(self, request, obj=None):
73 user_groupes = request.user.groups.all()
74 if grp_drh_recrutement in user_groupes or \
75 grp_administrateurs_recrutement in user_groupes:
76 return True
77 return False
78
79 class ProxyOffreEmploiAdmin(OffreEmploiAdmin):
80 list_display = ('nom', 'resume', 'date_limite', 'region', 'statut',
81 'est_affiche')
82 readonly_fields = ('description', 'poste', 'bureau',
83 'duree_affectation', 'renumeration',
84 'debut_affectation', 'lieu_affectation', 'nom',
85 'resume', 'date_limite', 'region')
86 fieldsets = (
87 ('Nom', {
88 'fields': ('nom', )
89 }),
90 ('Description générale', {
91 'fields': ('poste', 'resume','description', 'date_limite', )
92 }),
93 ('Coordonnées', {
94 'fields': ('lieu_affectation', 'bureau', 'region', )
95 }),
96 ('Autre', {
97 'fields': ('debut_affectation', 'duree_affectation',
98 'renumeration', )
99 }),
100 )
101 def has_add_permission(self, request):
102 return False
103
104 def has_delete_permission(self, request, obj=None):
105 return False
106
107 def has_change_permission(self, request, obj=None):
108 user_groupes = request.user.groups.all()
109 if grp_evaluateurs_recrutement in user_groupes or \
110 grp_drh_recrutement in user_groupes:
111 return True
112 return False
113
114 class ProxyCandidatPiece(CandidatPiece):
115 """
116 Ce proxy sert uniquement dans l'admin à disposer d'un libellé
117 plus ergonomique.
118 """
119 class Meta:
120 proxy = True
121 verbose_name = "pièce jointe"
122 verbose_name_plural = "pièces jointes"
123
124 class CandidatPieceInline(admin.TabularInline):
125 model = ProxyCandidatPiece
126 fields = ('candidat', 'nom', 'path', )
127 extra = 1
128
129 class ProxyEvaluateur(Evaluateur.candidats.through):
130 """
131 Ce proxy sert uniquement dans l'admin à disposer d'un libellé
132 plus ergonomique.
133 """
134 class Meta:
135 proxy = True
136 verbose_name = "évaluateur"
137
138 class EvaluateurInline(admin.TabularInline):
139 model = ProxyEvaluateur
140 fields = ('evaluateur',)
141 extra = 1
142
143 class CandidatAdmin(VersionAdmin):
144 date_hierarchy = 'date_creation'
145 list_display = ('nom', 'prenom', 'offre_emploi','statut',
146 'voir_offre_emploi', 'calculer_moyenne',
147 'afficher_candidat',)
148 list_filter = ('offre_emploi', )
149 fieldsets = (
150 ("Offre d'emploi", {
151 'fields': ('offre_emploi', )
152 }),
153 ('Informations personnelles', {
154 'fields': ('prenom','nom','genre', 'nationalite',
155 'situation_famille', 'nombre_dependant',)
156 }),
157 ('Coordonnées', {
158 'fields': ('telephone', 'email', 'adresse', 'ville',
159 'etat_province', 'code_postal', 'pays', )
160 }),
161 ('Informations professionnelles', {
162 'fields': ('niveau_diplome','employeur_actuel',
163 'poste_actuel', 'domaine_professionnel',)
164 }),
165 ('Traitement', {
166 'fields': ('statut', )
167 }),
168 )
169 inlines = [
170 CandidatPieceInline,
171 EvaluateurInline,
172 ]
173
174 actions = ['affecter_candidats_evaluateur', 'envoyer_courriel_candidats']
175 # Affecter un évaluateurs à des candidats
176 def affecter_candidats_evaluateur(modeladmin, obj, candidats):
177 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
178
179 return HttpResponseRedirect(reverse('affecter_evaluateurs_candidats')+
180 "?ids=%s" % (",".join(selected)))
181 affecter_candidats_evaluateur.short_description = u'Affecter évaluateur(s)'
182
183 # Envoyer un courriel à des candidats
184 def envoyer_courriel_candidats(modeladmin, obj, candidats):
185 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
186
187 return HttpResponseRedirect(reverse('selectionner_template')+
188 "?ids=%s" % (",".join(selected)))
189 envoyer_courriel_candidats.short_description = u'Envoyer courriel'
190
191 # Évaluer un candidat
192 def evaluer_candidat(self, obj):
193 return "<a href='%s?candidat__id__exact=%s'>Évaluer le candidat</a>" % \
194 (reverse('admin:recrutement_candidatevaluation_changelist'),
195 obj.id)
196 evaluer_candidat.allow_tags = True
197 evaluer_candidat.short_description = 'Évaluation'
198
199 # Afficher un candidat
200 def afficher_candidat(self, obj):
201 return "<a href='%s'>Voir le candidat</a>" % \
202 (reverse('admin:recrutement_proxycandidat_change', args=(obj.id,)))
203 afficher_candidat.allow_tags = True
204 afficher_candidat.short_description = u'Afficher les détails du candidat'
205
206 # Voir l'offre d'emploi
207 def voir_offre_emploi(self, obj):
208 return "<a href='%s'>Voir l'offre d'emploi</a>" % \
209 (reverse('admin:recrutement_proxyoffreemploi_change',
210 args=(obj.offre_emploi.id,)))
211 voir_offre_emploi.allow_tags = True
212 voir_offre_emploi.short_description = "Afficher l'offre d'emploi"
213
214 # Calculer la moyenne des notes
215 def calculer_moyenne(self, obj):
216 evaluations = CandidatEvaluation.objects.filter(candidat=obj)
217 offre_emploi = obj.offre_emploi
218
219 notes = [evaluation.note for evaluation in evaluations.all() \
220 if evaluation.note is not None]
221
222 if len(notes) > 0 and offre_emploi.date_limite <= datetime.date.today():
223 moyenne_votes = float(sum(notes)) / len(notes)
224 else:
225 moyenne_votes = "Non disponible"
226 return moyenne_votes
227 calculer_moyenne.allow_tags = True
228 calculer_moyenne.short_description = "Moyenne des notes"
229
230 def add_delete_permission(self, request, obj=None) :
231 user_groupes = request.user.groups.all()
232 if grp_drh_recrutement in user_groupes or \
233 grp_administrateurs_recrutement in user_groupes:
234 return True
235 return False
236
237 def has_add_permission(self, request):
238 return self.add_delete_permission(request, request)
239
240 def has_delete_permission(self, request, obj=None):
241 return self.add_delete_permission(request, request)
242
243 def has_change_permission(self, request, obj=None):
244 user_groupes = request.user.groups.all()
245 if grp_drh_recrutement in user_groupes or \
246 grp_administrateurs_recrutement in user_groupes:
247 return True
248 return False
249
250 def queryset(self, obj):
251 """
252 Spécifie un queryset limité, autrement Django exécute un
253 select_related() sans paramètre, ce qui a pour effet de charger tous
254 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
255 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
256 génération d'une requête infinie.
257
258 """
259 qs = self.model._default_manager.get_query_set()
260 # Si user est superuser afficher tous les candidats
261 user_groupes = obj.user.groups.all()
262 if not grp_drh_recrutement in user_groupes:
263 # Si le user n'est ni un évaluateur ni un administrateur régional,
264 # retourner none
265
266 # Vérifier groupes
267 if grp_evaluateurs_recrutement in user_groupes:
268 try:
269 user = Evaluateur.objects.get(user=obj.user)
270 except Evaluateur.DoesNotExist:
271 return qs.none()
272 """
273 elif grp_administrateurs_recrutement in user_groupes:
274 try:
275 user = AdministrateurRegional.objects.get(user=obj.user)
276 except AdministrateurRegional.DoesNotExist:
277 return qs.none()
278 """
279 else:
280 return qs.none()
281 ids = [c.id for c in user.candidats.all()]
282 return qs.select_related('candidats').filter(id__in=ids)
283 return qs.select_related('candidats')
284
285 class ProxyCandidatAdmin(CandidatAdmin):
286 readonly_fields = ('statut', 'offre_emploi', 'prenom', 'nom',
287 'genre', 'nationalite', 'situation_famille',
288 'nombre_dependant', 'telephone', 'email', 'adresse',
289 'ville', 'etat_province', 'code_postal', 'pays',
290 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
291 'domaine_professionnel',)
292 fieldsets = (
293 ("Offre d'emploi", {
294 'fields': ('offre_emploi', )
295 }),
296 ('Informations personnelles', {
297 'fields': ('prenom','nom','genre', 'nationalite',
298 'situation_famille', 'nombre_dependant',)
299 }),
300 ('Coordonnées', {
301 'fields': ('telephone', 'email', 'adresse', 'ville',
302 'etat_province', 'code_postal', 'pays', )
303 }),
304 ('Informations professionnelles', {
305 'fields': ('niveau_diplome','employeur_actuel',
306 'poste_actuel', 'domaine_professionnel',)
307 }),
308 )
309 inlines = []
310
311 def has_add_permission(self, request):
312 return False
313
314 def has_delete_permission(self, request, obj=None):
315 return False
316
317 def has_change_permission(self, request, obj=None):
318 user_groupes = request.user.groups.all()
319 if grp_drh_recrutement in user_groupes or \
320 grp_administrateurs_recrutement in user_groupes or \
321 grp_evaluateurs_recrutement in user_groupes:
322 return True
323 return False
324
325 class CandidatPieceAdmin(admin.ModelAdmin):
326 list_display = ('nom', 'candidat', )
327
328 def queryset(self, request):
329 """
330 Spécifie un queryset limité, autrement Django exécute un
331 select_related() sans paramètre, ce qui a pour effet de charger tous
332 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
333 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
334 génération d'une requête infinie.
335 Affiche la liste de candidats que si le user connecté
336 possède un Evaluateur
337 """
338 qs = self.model._default_manager.get_query_set()
339 return qs.select_related('candidat')
340
341 class EvaluateurAdmin(VersionAdmin):
342 fieldsets = (
343 (None, {'fields': ('user', )}),
344 #(None, {'fields': ('candidats',)}),
345 )
346
347 class AdministrateurRegionalAdmin(VersionAdmin):
348 pass
349
350 class CandidatEvaluationAdmin(VersionAdmin):
351 list_display = ('_offre_emploi', '_candidat', 'evaluateur', '_note',
352 '_commentaire', )
353 readonly_fields = ('candidat', 'evaluateur')
354 fieldsets = (
355 ('Évaluation du candidat', {
356 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
357 }),
358 )
359
360 def _note(self, obj):
361 """
362 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
363 un lien pour Évaluer le candidat.
364 Sinon afficher la note.
365 """
366 evaluateur = obj.evaluateur
367 candidat = obj.candidat
368 candidat_evaluation = CandidatEvaluation.objects.\
369 get(candidat=candidat, evaluateur=evaluateur)
370 if obj.note is None:
371 return "<a href='%s'>Candidat non évalué</a>" % \
372 (reverse('admin:recrutement_candidatevaluation_change',
373 args=(candidat_evaluation.id,)))
374 return "<a href='%s'>%s</a>" % \
375 (reverse('admin:recrutement_candidatevaluation_change',
376 args=(candidat_evaluation.id,)), obj.note)
377 return
378 _note.allow_tags = True
379 _note.short_description = "Votre note"
380 _note.admin_order_field = 'note'
381
382 def _candidat(self, obj):
383 return "<a href='%s'>%s</a>" \
384 % (reverse('admin:recrutement_proxycandidat_change',
385 args=(obj.candidat.id,)), obj.candidat)
386 _candidat.allow_tags = True
387 _candidat.short_description = 'Candidat'
388
389 def _commentaire(self, obj):
390 """
391 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
392 dans le champ commentaire, Aucun au lieu de (None)
393 Sinon afficher la note.
394 """
395 if obj.commentaire is None:
396 return "Aucun"
397 return obj.commentaire
398 _commentaire.allow_tags = True
399 _commentaire.short_description = "Commentaire"
400
401
402 def _offre_emploi(self, obj):
403 return "<a href='%s'>%s</a>" % \
404 (reverse('admin:recrutement_proxyoffreemploi_change',
405 args=(obj.candidat.offre_emploi.id,)), obj.candidat.offre_emploi)
406 _offre_emploi.allow_tags = True
407 _offre_emploi.short_description = "Voir offre d'emploi"
408 _offre_emploi.admin_order_field = 'offre_emploi'
409
410 def has_change_permission(self, request, obj=None):
411 """
412 Permettre la visualisation dans la changelist
413 mais interdire l'accès à modifier l'objet si l'évaluateur n'est pas
414 le request.user
415 """
416 return obj is None or request.user == obj.evaluateur.user
417
418 def queryset(self, request):
419 """
420 Afficher uniquement les évaluations de l'évaluateur, sauf si
421 l'utilisateur est super admin.
422 """
423 qs = self.model._default_manager.get_query_set()
424 user_groupes = request.user.groups.all()
425 if grp_drh_recrutement in user_groupes:
426 return qs.select_related('offre_emploi')
427
428 try:
429 evaluateur = Evaluateur.objects.get(user=request.user)
430 except Evaluateur.DoesNotExist:
431 return qs.none()
432
433 candidats_evaluations = CandidatEvaluation.objects.\
434 filter(evaluateur=evaluateur)
435 candidats_evaluations_ids = [ce.id for ce in \
436 candidats_evaluations.all()]
437 return qs.select_related('offre_emploi').\
438 filter(id__in=candidats_evaluations_ids)
439
440 class CourrielTemplateAdmin(VersionAdmin):
441 pass
442
443 admin.site.register(OffreEmploi, OffreEmploiAdmin)
444 admin.site.register(ProxyOffreEmploi, ProxyOffreEmploiAdmin)
445 admin.site.register(Candidat, CandidatAdmin)
446 admin.site.register(ProxyCandidat, ProxyCandidatAdmin)
447 admin.site.register(CandidatEvaluation, CandidatEvaluationAdmin)
448 admin.site.register(Evaluateur, EvaluateurAdmin)
449 #admin.site.register(AdministrateurRegional, AdministrateurRegionalAdmin)
450 admin.site.register(CourrielTemplate, CourrielTemplateAdmin)