1750: Un évaluateur doit être assignés à des offres d'emploi et non à des candidats.
[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 from django.core.files.storage import default_storage
8
9 from reversion.admin import VersionAdmin
10 from datamaster_modeles.models import Employe, Implantation, Region
11 from django.forms.models import BaseInlineFormSet
12
13 from recrutement.models import *
14 from recrutement.workflow import grp_administrateurs_recrutement,\
15 grp_evaluateurs_recrutement, grp_drh_recrutement
16
17 """
18 class MetaAdmin(VersionAdmin):
19 def get_actions(self, request):
20
21 Pour refactoring
22 """
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', ]
29
30 def get_actions(self, request):
31 actions = super(OffreEmploiAdmin, self).get_actions(request)
32 del actions['delete_selected']
33 return actions
34
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)
38
39 return HttpResponseRedirect(reverse('affecter_evaluateurs_offre_emploi')+
40 "?ids=%s" % (",".join(selected)))
41 affecter_evaluateurs_offre_emploi.short_description = u'Affecter évaluateur(s)'
42
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"
49
50
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:
57 """
58 Si le user n'est ni un évaluateur ni un administrateur régional,
59 retourner none
60 Vérifier groupes
61 """
62 if grp_evaluateurs_recrutement in user_groupes:
63 try:
64 user = Evaluateur.objects.get(user=request.user)
65 except Evaluateur.DoesNotExist:
66 return qs.none()
67 elif grp_administrateurs_recrutement in user_groupes:
68 try:
69 user = AdministrateurRegional.objects.get(user=request.user)
70 except AdministrateurRegional.DoesNotExist:
71 return qs.none()
72 else:
73 return qs.none()
74
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)
84 return qs.none()
85 return qs.select_related('offre_emploi')
86
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:
92 return True
93 return False
94
95 class ProxyOffreEmploiAdmin(OffreEmploiAdmin):
96 list_display = ('nom', 'resume', 'date_limite', 'region', 'statut',
97 'est_affiche')
98 readonly_fields = ('description', 'bureau',
99 'duree_affectation', 'renumeration',
100 'debut_affectation', 'lieu_affectation', 'nom',
101 'resume', 'date_limite', 'region')
102 fieldsets = (
103 ('Nom', {
104 'fields': ('nom', )
105 }),
106 ('Description générale', {
107 'fields': ('resume','description', 'date_limite', )
108 }),
109 ('Coordonnées', {
110 'fields': ('lieu_affectation', 'bureau', 'region', )
111 }),
112 ('Autre', {
113 'fields': ('debut_affectation', 'duree_affectation',
114 'renumeration', )
115 }),
116 )
117
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'))
125
126 def has_add_permission(self, request):
127 return False
128
129 def has_delete_permission(self, request, obj=None):
130 return False
131
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:
137 return True
138 return False
139
140 """class ProxyEvaluateur(Evaluateur.candidats.through):
141
142 Ce proxy sert uniquement dans l'admin à disposer d'un libellé
143 plus ergonomique.
144
145 class Meta:
146 proxy = True
147 verbose_name = "évaluateur"
148 """
149 class CandidatPieceInline(admin.TabularInline):
150 model = CandidatPiece
151 fields = ('candidat', 'nom', 'path',)
152 extra = 1
153 max_num = 3
154
155 class CandidatEvaluationInlineFormSet(BaseInlineFormSet):
156 """
157 Empêche la suppression d'une évaluation pour le CandidatEvaluationInline
158 """
159 def __init__(self, *args, **kwargs):
160 super(CandidatEvaluationInlineFormSet, self).__init__(*args, **kwargs)
161 self.can_delete = False
162
163 class CandidatEvaluationInline(admin.TabularInline):
164 model = CandidatEvaluation
165 fields = ('evaluateur', 'note', 'commentaire')
166 max_num = 0
167 extra = 0
168 formset = CandidatEvaluationInlineFormSet
169
170 def get_readonly_fields(self, request, obj=None):
171 """
172 Empêche la modification des évaluations
173 """
174 if obj:
175 return self.readonly_fields+('evaluateur', 'note', 'commentaire')
176 return self.readonly_fields
177
178
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', )
185 fieldsets = (
186 ("Offre d'emploi", {
187 'fields': ('offre_emploi', )
188 }),
189 ('Informations personnelles', {
190 'fields': ('prenom','nom','genre', 'nationalite',
191 'situation_famille', 'nombre_dependant',)
192 }),
193 ('Coordonnées', {
194 'fields': ('telephone', 'email', 'adresse', 'ville',
195 'etat_province', 'code_postal', 'pays', )
196 }),
197 ('Informations professionnelles', {
198 'fields': ('niveau_diplome','employeur_actuel',
199 'poste_actuel', 'domaine_professionnel',)
200 }),
201 ('Traitement', {
202 'fields': ('statut', )
203 }),
204 )
205 inlines = [
206 CandidatPieceInline,
207 CandidatEvaluationInline,
208 ]
209
210 actions = ['envoyer_courriel_candidats']
211
212 def get_actions(self, request):
213 actions = super(CandidatAdmin, self).get_actions(request)
214 del actions['delete_selected']
215 return actions
216
217 # Envoyer un courriel à des candidats
218 def envoyer_courriel_candidats(modeladmin, obj, candidats):
219 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
220
221 return HttpResponseRedirect(reverse('selectionner_template')+
222 "?ids=%s" % (",".join(selected)))
223 envoyer_courriel_candidats.short_description = u'Envoyer courriel'
224
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'),
229 obj.id)
230 evaluer_candidat.allow_tags = True
231 evaluer_candidat.short_description = 'Évaluation'
232
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'
239
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"
247
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
252
253 notes = [evaluation.note for evaluation in evaluations.all() \
254 if evaluation.note is not None]
255
256 if len(notes) > 0 and offre_emploi.date_limite <= datetime.date.today():
257 moyenne_votes = float(sum(notes)) / len(notes)
258 else:
259 moyenne_votes = "Non disponible"
260 return moyenne_votes
261 calculer_moyenne.allow_tags = True
262 calculer_moyenne.short_description = "Moyenne des notes"
263
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:
269 return True
270 return False
271
272 def has_add_permission(self, request):
273 return self.add_delete_permission(request, request)
274
275 def has_delete_permission(self, request, obj=None):
276 return self.add_delete_permission(request, request)
277
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:
283 return True
284 return False
285
286 def queryset(self, request):
287 """
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.
293
294 """
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,
301 # retourner none
302
303 # Vérifier groupes
304 if grp_evaluateurs_recrutement in user_groupes:
305 try:
306 user = Evaluateur.objects.get(user=request.user)
307 except Evaluateur.DoesNotExist:
308 return qs.none()
309 """
310 elif grp_administrateurs_recrutement in user_groupes:
311 try:
312 user = AdministrateurRegional.objects.get(user=obj.user)
313 except AdministrateurRegional.DoesNotExist:
314 return qs.none()
315 """
316 else:
317 return qs.none()
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')
321
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',)
329 fieldsets = (
330 ("Offre d'emploi", {
331 'fields': ('offre_emploi', )
332 }),
333 ('Informations personnelles', {
334 'fields': ('prenom','nom','genre', 'nationalite',
335 'situation_famille', 'nombre_dependant',)
336 }),
337 ('Coordonnées', {
338 'fields': ('telephone', 'email', 'adresse', 'ville',
339 'etat_province', 'code_postal', 'pays', )
340 }),
341 ('Informations professionnelles', {
342 'fields': ('niveau_diplome','employeur_actuel',
343 'poste_actuel', 'domaine_professionnel',)
344 }),
345 )
346 inlines = []
347
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'))
355
356 def has_add_permission(self, request):
357 return False
358
359 def has_delete_permission(self, request, obj=None):
360 return False
361
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:
368 return True
369 return False
370
371 class CandidatPieceAdmin(admin.ModelAdmin):
372 list_display = ('nom', 'candidat', )
373
374 def queryset(self, request):
375 """
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
383 """
384 qs = self.model._default_manager.get_query_set()
385 return qs.select_related('candidat')
386
387 class EvaluateurAdmin(VersionAdmin):
388 fieldsets = (
389 ("Utilisateur", {
390 'fields': ('user',)
391 }),
392 ("Offres d'emploi à évaluer", {
393 'fields': ('offres_emploi',)
394 }),
395 )
396
397 def get_actions(self, request):
398 actions = super(EvaluateurAdmin, self).get_actions(request)
399 del actions['delete_selected']
400 return actions
401
402 class AdministrateurRegionalAdmin(VersionAdmin):
403 pass
404
405 class CandidatEvaluationAdmin(VersionAdmin):
406 list_display = ('_candidat', '_offre_emploi', 'evaluateur', '_note',
407 '_commentaire', )
408 readonly_fields = ('candidat', 'evaluateur')
409 fieldsets = (
410 ('Évaluation du candidat', {
411 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
412 }),
413 )
414
415 def get_actions(self, request):
416 actions = super(CandidatEvaluationAdmin, self).get_actions(request)
417 del actions['delete_selected']
418 return actions
419
420 def _note(self, obj):
421 """
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.
425 """
426 evaluateur = obj.evaluateur
427 candidat = obj.candidat
428 candidat_evaluation = CandidatEvaluation.objects.\
429 get(candidat=candidat, evaluateur=evaluateur)
430 if obj.note is None:
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)
437 return
438 _note.allow_tags = True
439 _note.short_description = "Votre note"
440 _note.admin_order_field = 'note'
441
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'
448
449 def _commentaire(self, obj):
450 """
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.
454 """
455 if obj.commentaire is None:
456 return "Aucun"
457 return obj.commentaire
458 _commentaire.allow_tags = True
459 _commentaire.short_description = "Commentaire"
460
461
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"
468
469 def has_change_permission(self, request, obj=None):
470 """
471 Permettre la visualisation dans la changelist
472 mais interdire l'accès à modifier l'objet si l'évaluateur n'est pas
473 le request.user
474 """
475 return obj is None or request.user == obj.evaluateur.user
476
477 def queryset(self, request):
478 """
479 Afficher uniquement les évaluations de l'évaluateur, sauf si
480 l'utilisateur est super admin.
481 """
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')
487
488 try:
489 evaluateur = Evaluateur.objects.get(user=request.user)
490 except Evaluateur.DoesNotExist:
491 return qs.none()
492
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)
499
500 class CourrielTemplateAdmin(VersionAdmin):
501 def get_actions(self, request):
502 actions = super(CourrielTemplateAdmin, self).get_actions(request)
503 del actions['delete_selected']
504 return actions
505
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)