Fix Export buttons bug
[auf_rh_dae.git] / project / recrutement / admin.py
1 # -*- encoding: utf-8 -*-
2
3 import textwrap
4
5 from auf.django.emploi.models import OffreEmploi, Candidat, CandidatPiece
6 from auf.django.references.models import Region, Bureau
7 from django.conf import settings
8 from django.contrib import admin
9 from django.core.urlresolvers import reverse
10 from django.db.models import Avg
11 from django.shortcuts import render_to_response
12 from django.template import RequestContext
13
14 from auf.django.emploi.models import STATUT_CHOICES
15 from django.forms.models import BaseInlineFormSet
16 from django.http import HttpResponseRedirect
17 from django.shortcuts import redirect
18 from reversion.admin import VersionAdmin
19
20 from auf.django.emploi.models import OffreEmploi, Candidat, CandidatPiece
21 from auf.django.references.models import Region, Bureau, Implantation
22 from auf.django.export.admin import ExportAdmin
23
24 from project.groups import get_employe_from_user as get_emp
25 from project.rh import models as rh
26 from project.recrutement.forms import OffreEmploiForm
27 from project.recrutement.groups import \
28 grp_drh, grp_drh2, \
29 grp_directeurs_bureau, \
30 grp_administrateurs, \
31 grp_correspondants_rh, \
32 grp_haute_direction
33 from project.recrutement.models import \
34 Evaluateur, CandidatEvaluation, \
35 ProxyOffreEmploi, ProxyCandidat, MesCandidatEvaluation, \
36 CourrielTemplate
37
38 ### CONSTANTES
39 IMPLANTATIONS_CENTRALES = [15, 19]
40
41
42 class BaseAdmin(admin.ModelAdmin):
43
44 class Media:
45 css = {'screen': (
46 'css/admin_custom.css',
47 'jquery-autocomplete/jquery.autocomplete.css',
48 )}
49 js = (
50 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js',
51 'jquery-autocomplete/jquery.autocomplete.min.js',
52 )
53
54
55 class OrderedChangeList(admin.views.main.ChangeList):
56 """
57 Surcharge pour appliquer le order_by d'un annotate
58 """
59 def get_query_set(self):
60 qs = super(OrderedChangeList, self).get_query_set()
61 qs = qs.order_by('-moyenne')
62 return qs
63
64
65 class OffreEmploiAdminMixin(BaseAdmin):
66 date_hierarchy = 'date_creation'
67 list_display = (
68 'nom', 'date_limite', 'region', 'statut', 'est_affiche',
69 '_candidatsList'
70 )
71 exclude = ('actif', 'poste_nom', 'resume',)
72 list_filter = ('statut',)
73 actions = ['affecter_evaluateurs_offre_emploi', ]
74 form = OffreEmploiForm
75 fieldsets = (
76 (None, {
77 'fields': (
78 'est_affiche',
79 'statut',
80 'date_limite',
81 'nom',
82 'description',
83 'poste',
84 'region',
85 'lieu_affectation',
86 'bureau',
87 'debut_affectation',
88 'duree_affectation',
89 'renumeration',
90 )
91 }),
92 )
93
94 ### Actions à afficher
95 def get_actions(self, request):
96 actions = super(OffreEmploiAdminMixin, self).get_actions(request)
97 del actions['delete_selected']
98 return actions
99
100 ### Affecter un évaluateurs à des offres d'emploi
101 def affecter_evaluateurs_offre_emploi(modeladmin, obj, candidats):
102 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
103
104 return HttpResponseRedirect(
105 reverse('affecter_evaluateurs_offre_emploi') +
106 "?ids=%s" % (",".join(selected))
107 )
108
109 affecter_evaluateurs_offre_emploi.short_description = \
110 u'Affecter évaluateur(s)'
111
112 ### Afficher la liste des candidats pour l'offre d'emploi
113 def _candidatsList(self, obj):
114 return "<a href='%s?offre_emploi__id__exact=%s'>Voir les candidats \
115 </a>" % (reverse('admin:emploi_candidat_changelist'), obj.id)
116 _candidatsList.allow_tags = True
117 _candidatsList.short_description = "Afficher la liste des candidats"
118
119 ### Formulaire
120 def get_form(self, request, obj=None, **kwargs):
121 form = super(OffreEmploiAdminMixin, self).get_form(request, obj, **kwargs)
122 employe = get_emp(request.user)
123 user_groupes = request.user.groups.all()
124
125 # Region
126 if 'region' in form.declared_fields:
127 region_field = form.declared_fields['region']
128 else:
129 region_field = form.base_fields['region']
130
131 if grp_drh in user_groupes or \
132 grp_drh2 in user_groupes or \
133 grp_haute_direction in user_groupes:
134 region_field.queryset = Region.objects.all()
135 else:
136 region_field.queryset = Region.objects.\
137 filter(id=employe.implantation.region.id)
138
139 # Poste
140 if 'poste' in form.declared_fields:
141 poste_field = form.declared_fields['poste']
142 else:
143 poste_field = form.base_fields['poste']
144
145 if grp_drh in user_groupes or \
146 grp_drh2 in user_groupes or \
147 grp_haute_direction in user_groupes:
148 poste_field.queryset = rh.Poste.objects.all()
149 else:
150 poste_field.queryset = rh.Poste.objects.\
151 filter(implantation__region=employe.implantation.region).\
152 exclude(implantation__in=IMPLANTATIONS_CENTRALES)
153
154 # Bureau
155 if 'bureau' in form.declared_fields:
156 bureau_field = form.declared_fields['bureau']
157 else:
158 bureau_field = form.base_fields['bureau']
159
160 if grp_drh in user_groupes or \
161 grp_drh2 in user_groupes or \
162 grp_haute_direction in user_groupes:
163 bureau_field.queryset = Bureau.objects.all()
164 else:
165 bureau_field.queryset = \
166 Bureau.objects.filter(region=employe.implantation.region)
167
168 return form
169
170 ### Queryset
171
172 def queryset(self, request):
173 qs = self.model._default_manager.get_query_set() \
174 .select_related('offre_emploi')
175 user_groupes = request.user.groups.all()
176 if grp_drh in user_groupes or \
177 grp_drh2 in user_groupes or \
178 grp_haute_direction in user_groupes:
179 return qs
180
181 if grp_directeurs_bureau in user_groupes or \
182 grp_correspondants_rh in user_groupes or \
183 grp_administrateurs in user_groupes:
184 employe = get_emp(request.user)
185 return qs.filter(region=employe.implantation.region)
186
187 if Evaluateur.objects.filter(user=request.user).exists():
188 evaluateur = Evaluateur.objects.get(user=request.user)
189 offre_ids = [
190 e.candidat.offre_emploi_id
191 for e in CandidatEvaluation.objects
192 .select_related('candidat')
193 .filter(evaluateur=evaluateur)
194 ]
195 return qs.filter(id__in=offre_ids)
196
197 return qs.none()
198
199 ### Permission add, delete, change
200 def has_add_permission(self, request):
201 user_groupes = request.user.groups.all()
202 if request.user.is_superuser is True or \
203 grp_drh in user_groupes or \
204 grp_drh2 in user_groupes or \
205 grp_directeurs_bureau in user_groupes or \
206 grp_administrateurs in user_groupes or \
207 grp_haute_direction in user_groupes:
208 return True
209 return False
210
211 def has_delete_permission(self, request, obj=None):
212 user_groupes = request.user.groups.all()
213 if request.user.is_superuser is True or \
214 grp_drh in user_groupes or \
215 grp_drh2 in user_groupes or \
216 grp_directeurs_bureau in user_groupes or \
217 grp_administrateurs in user_groupes or \
218 grp_haute_direction in user_groupes:
219 return True
220 return False
221
222 def has_change_permission(self, request, obj=None):
223 user_groupes = request.user.groups.all()
224 if request.user.is_superuser is True or \
225 grp_drh in user_groupes or \
226 grp_drh2 in user_groupes or \
227 grp_directeurs_bureau in user_groupes or \
228 grp_administrateurs in user_groupes or \
229 grp_haute_direction in user_groupes:
230 return True
231 return False
232
233 def formfield_for_foreignkey(self, db_field, request, **kwargs):
234 if db_field.name == 'lieu_affectation':
235 employe = get_emp(request.user)
236 kwargs["queryset"] = Implantation.objects.filter(region=employe.implantation.region)
237 return db_field.formfield(**kwargs)
238 return super(OffreEmploiAdminMixin, self).formfield_for_foreignkey(db_field, request, **kwargs)
239
240
241 class OffreEmploiAdmin(VersionAdmin, OffreEmploiAdminMixin):
242 pass
243
244
245 class ProxyOffreEmploiAdmin(OffreEmploiAdminMixin):
246 list_display = (
247 'nom', 'date_limite', 'region', 'statut', 'est_affiche'
248 )
249 readonly_fields = (
250 'description', 'bureau', 'duree_affectation', 'renumeration',
251 'debut_affectation', 'lieu_affectation', 'nom', 'resume',
252 'date_limite', 'region', 'poste'
253 )
254 fieldsets = (
255 ('Nom', {
256 'fields': ('nom',)
257 }),
258 ('Description générale', {
259 'fields': ('description', 'date_limite',)
260 }),
261 ('Coordonnées', {
262 'fields': ('lieu_affectation', 'bureau', 'region', 'poste',)
263 }),
264 ('Autre', {
265 'fields': (
266 'debut_affectation', 'duree_affectation', 'renumeration',
267 )
268 }),
269 )
270 inlines = []
271
272 ### Lieu de redirection après le change
273 def response_change(self, request, obj):
274 return redirect('admin:recrutement_proxyoffreemploi_changelist')
275
276 ### Formulaire
277 def get_form(self, request, obj=None, **kwargs):
278 form = super(OffreEmploiAdmin, self).get_form(request, obj, **kwargs)
279 return form
280
281 ### Permissions add, delete, change
282 def has_add_permission(self, request):
283 return False
284
285 def has_delete_permission(self, request, obj=None):
286 return False
287
288 def has_change_permission(self, request, obj=None):
289 if obj is not None:
290 return True
291
292 return not super(ProxyOffreEmploiAdmin, self).has_change_permission(request, obj)
293
294
295 class CandidatPieceInline(admin.TabularInline):
296 model = CandidatPiece
297 fields = ('candidat', 'nom', 'path',)
298 extra = 1
299 max_num = 3
300
301
302 class ReadOnlyCandidatPieceInline(CandidatPieceInline):
303 readonly_fields = ('candidat', 'nom', 'path', )
304 cand_delete = False
305
306
307 class CandidatEvaluationInlineFormSet(BaseInlineFormSet):
308 """
309 Empêche la suppression d'une évaluation pour le CandidatEvaluationInline
310 """
311 def __init__(self, *args, **kwargs):
312 super(CandidatEvaluationInlineFormSet, self).__init__(*args, **kwargs)
313 self.can_delete = False
314
315
316 class CandidatEvaluationInline(admin.TabularInline):
317 model = CandidatEvaluation
318 fields = ('evaluateur', 'note', 'commentaire')
319 max_num = 0
320 extra = 0
321 formset = CandidatEvaluationInlineFormSet
322
323 ### Fields readonly
324 def get_readonly_fields(self, request, obj=None):
325 """
326 Empêche la modification des évaluations
327 """
328 if obj:
329 return self.readonly_fields + ('evaluateur', 'note', 'commentaire')
330 return self.readonly_fields
331
332
333 class CandidatAdminMixin(BaseAdmin, ExportAdmin):
334 search_fields = ('nom', 'prenom')
335 exclude = ('actif', )
336 list_editable = ('statut', )
337 list_display = ('_candidat', 'offre_emploi',
338 'voir_offre_emploi', 'calculer_moyenne',
339 'afficher_candidat', '_date_creation', 'statut', )
340 list_filter = ('offre_emploi__nom', 'offre_emploi__region', 'statut', )
341
342 fieldsets = (
343 ("Offre d'emploi", {
344 'fields': ('offre_emploi', )
345 }),
346 ('Informations personnelles', {
347 'fields': (
348 'nom', 'prenom', 'genre', 'nationalite',
349 'situation_famille', 'nombre_dependant'
350 )
351 }),
352 ('Coordonnées', {
353 'fields': (
354 'telephone', 'email', 'adresse', 'ville', 'etat_province',
355 'code_postal', 'pays'
356 )
357 }),
358 ('Informations professionnelles', {
359 'fields': (
360 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
361 'domaine_professionnel'
362 )
363 }),
364 ('Traitement', {
365 'fields': ('statut', )
366 }),
367 )
368 inlines = [
369 CandidatPieceInline,
370 CandidatEvaluationInline,
371 ]
372 actions = ['envoyer_courriel_candidats', 'changer_statut']
373
374 export_fields = ['statut', 'offre_emploi', 'prenom', 'nom', 'genre',
375 'nationalite', 'situation_famille', 'nombre_dependant',
376 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
377 'domaine_professionnel', 'telephone', 'email', 'adresse',
378 'ville', 'etat_province', 'code_postal', 'pays']
379
380 def _candidat(self, obj):
381 txt = u"%s %s (%s)" % (obj.nom.upper(), obj.prenom, obj.genre)
382 txt = textwrap.wrap(txt, 30)
383 return "<br/>".join(txt)
384 _candidat.short_description = "Candidat"
385 _candidat.admin_order_field = "nom"
386 _candidat.allow_tags = True
387
388 def _date_creation(self, obj):
389 return obj.date_creation
390 _date_creation.admin_order_field = "date_creation"
391 _date_creation.short_description = "Date de réception"
392
393 ### Actions à afficher
394 def get_actions(self, request):
395 actions = super(CandidatAdminMixin, self).get_actions(request)
396 del actions['delete_selected']
397 return actions
398
399 ### Envoyer un courriel à des candidats
400 def envoyer_courriel_candidats(modeladmin, obj, candidats):
401 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
402
403 return HttpResponseRedirect(
404 reverse('selectionner_template') + "?ids=%s" % (",".join(selected))
405 )
406 envoyer_courriel_candidats.short_description = u'Envoyer courriel'
407
408 ### Changer le statut à des candidats
409 def changer_statut(modeladmin, request, queryset):
410 if request.POST.get('post'):
411 queryset.update(statut=request.POST.get('statut'))
412 return None
413
414 context = {
415 'action_checkbox_name': admin.helpers.ACTION_CHECKBOX_NAME,
416 'queryset': queryset,
417 'status': STATUT_CHOICES,
418 }
419
420 return render_to_response("recrutement/selectionner_statut.html",
421 context, context_instance = RequestContext(request))
422
423 changer_statut.short_description = u'Changer statut'
424
425 ### Évaluer un candidat
426 def evaluer_candidat(self, obj):
427 return "<a href='%s?candidat__id__exact=%s'>" \
428 "Évaluer le candidat</a>" % (
429 reverse('admin:recrutement_candidatevaluation_changelist'),
430 obj.id
431 )
432 evaluer_candidat.allow_tags = True
433 evaluer_candidat.short_description = 'Évaluation'
434
435 ### Afficher un candidat
436 def afficher_candidat(self, obj):
437 items = [u"<li><a href='%s%s'>%s</li>" % \
438 (settings.OE_PRIVE_MEDIA_URL, pj.path, pj.get_nom_display()) \
439 for pj in obj.pieces_jointes()]
440 html = "<a href='%s'>Candidature</a>" % (
441 reverse('admin:recrutement_proxycandidat_change', args=(obj.id,))
442 )
443 return "%s<ul>%s</ul>" % (html, "\n".join(items))
444 afficher_candidat.allow_tags = True
445 afficher_candidat.short_description = u'Détails du candidat'
446
447 ### Voir l'offre d'emploi
448 def voir_offre_emploi(self, obj):
449 return "<a href='%s'>Voir l'offre d'emploi</a>" % (reverse(
450 'admin:recrutement_proxyoffreemploi_change',
451 args=(obj.offre_emploi.id,)
452 ))
453 voir_offre_emploi.allow_tags = True
454 voir_offre_emploi.short_description = "Afficher l'offre d'emploi"
455
456 ### Calculer la moyenne des notes
457 def calculer_moyenne(self, obj):
458 evaluations = CandidatEvaluation.objects.filter(candidat=obj)
459
460 notes = [evaluation.note for evaluation in evaluations \
461 if evaluation.note is not None]
462
463 if len(notes) > 0:
464 moyenne_votes = round(float(sum(notes)) / len(notes), 2)
465 else:
466 moyenne_votes = "Non disponible"
467
468 totales = len(evaluations)
469 faites = len(notes)
470
471 if obj.statut == 'REC':
472 if totales == faites:
473 color = "green"
474 elif faites > 0 and float(totales) / float(faites) >= 2:
475 color = "orange"
476 else:
477 color = "red"
478 else:
479 color = "black"
480
481 return """<span style="color: %s;">%s (%s/%s)</span>""" % (
482 color, moyenne_votes, faites, totales
483 )
484 calculer_moyenne.allow_tags = True
485 calculer_moyenne.short_description = "Moyenne"
486 calculer_moyenne.admin_order_field = ""
487
488 ### Permissions add, delete, change
489 def has_add_permission(self, request):
490 user_groupes = request.user.groups.all()
491 if request.user.is_superuser is True or \
492 grp_correspondants_rh in user_groupes or \
493 grp_drh in user_groupes or \
494 grp_drh2 in user_groupes or \
495 grp_directeurs_bureau in user_groupes or \
496 grp_administrateurs in user_groupes or \
497 grp_haute_direction in user_groupes:
498 return True
499 return False
500
501 def has_delete_permission(self, request, obj=None):
502 user_groupes = request.user.groups.all()
503 if request.user.is_superuser is True or \
504 grp_correspondants_rh in user_groupes or \
505 grp_drh in user_groupes or \
506 grp_drh2 in user_groupes or \
507 grp_directeurs_bureau in user_groupes or \
508 grp_administrateurs in user_groupes or \
509 grp_haute_direction in user_groupes:
510 return True
511 return False
512
513 def has_change_permission(self, request, obj=None):
514 user_groupes = request.user.groups.all()
515 if request.user.is_superuser is True or \
516 grp_correspondants_rh in user_groupes or \
517 grp_drh in user_groupes or \
518 grp_drh2 in user_groupes or \
519 grp_directeurs_bureau in user_groupes or \
520 grp_administrateurs in user_groupes or \
521 grp_haute_direction in user_groupes:
522 return True
523 return False
524
525 def formfield_for_foreignkey(self, db_field, request, **kwargs):
526 if db_field.name == 'offre_emploi':
527 employe = get_emp(request.user)
528 kwargs["queryset"] = OffreEmploi.objects.filter(region=employe.implantation.region)
529 return db_field.formfield(**kwargs)
530 return super(CandidatAdminMixin, self).formfield_for_foreignkey(db_field, request, **kwargs)
531
532 def get_changelist(self, request, **kwargs):
533 return OrderedChangeList
534
535 def queryset(self, request):
536 """
537 Spécifie un queryset limité, autrement Django exécute un
538 select_related() sans paramètre, ce qui a pour effet de charger tous
539 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
540 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
541 génération d'une requête infinie.
542 """
543 qs = self.model._default_manager.get_query_set() \
544 .select_related('offre_emploi') \
545 .annotate(moyenne=Avg('evaluations__note'))
546
547 user_groupes = request.user.groups.all()
548 if grp_drh in user_groupes or \
549 grp_drh2 in user_groupes or \
550 grp_haute_direction in user_groupes:
551 return qs
552
553 if grp_directeurs_bureau in user_groupes or \
554 grp_correspondants_rh in user_groupes or \
555 grp_administrateurs in user_groupes:
556 employe = get_emp(request.user)
557 return qs.filter(offre_emploi__region=employe.implantation.region)
558
559 if Evaluateur.objects.filter(user=request.user).exists():
560 evaluateur = Evaluateur.objects.get(user=request.user)
561 candidat_ids = [e.candidat.id for e in
562 CandidatEvaluation.objects.filter(evaluateur=evaluateur)]
563 return qs.filter(id__in=candidat_ids)
564 return qs.none()
565
566
567 class CandidatAdmin(VersionAdmin, CandidatAdminMixin):
568 change_list_template = 'admin/recrutement/candidat/change_list.html'
569 pass
570
571
572 class ProxyCandidatAdmin(CandidatAdminMixin):
573 change_list_template = 'admin/recrutement/candidat/change_list.html'
574 list_editable = ()
575 readonly_fields = (
576 'statut', 'offre_emploi', 'prenom', 'nom', 'genre', 'nationalite',
577 'situation_famille', 'nombre_dependant', 'telephone', 'email',
578 'adresse', 'ville', 'etat_province', 'code_postal', 'pays',
579 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
580 'domaine_professionnel', 'pieces_jointes'
581 )
582 fieldsets = (
583 ("Offre d'emploi", {
584 'fields': ('offre_emploi', )
585 }),
586 ('Informations personnelles', {
587 'fields': (
588 'prenom', 'nom', 'genre', 'nationalite', 'situation_famille',
589 'nombre_dependant'
590 )
591 }),
592 ('Coordonnées', {
593 'fields': (
594 'telephone', 'email', 'adresse', 'ville', 'etat_province',
595 'code_postal', 'pays'
596 )
597 }),
598 ('Informations professionnelles', {
599 'fields': (
600 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
601 'domaine_professionnel'
602 )
603 }),
604 )
605 inlines = (CandidatEvaluationInline, )
606
607 def has_add_permission(self, request):
608 return False
609
610 def has_delete_permission(self, request, obj=None):
611 return False
612
613 def has_change_permission(self, request, obj=None):
614 if obj is not None:
615 evaluateur = Evaluateur.objects.get(user=request.user)
616 for e in obj.evaluations.all():
617 if e.evaluateur == evaluateur:
618 return True
619
620 return not super(ProxyCandidatAdmin, self).has_change_permission(request, obj)
621
622 def get_actions(self, request):
623 return None
624
625
626 class CandidatPieceAdmin(admin.ModelAdmin):
627 list_display = ('nom', 'candidat', )
628
629 ### Queryset
630 def queryset(self, request):
631 """
632 Spécifie un queryset limité, autrement Django exécute un
633 select_related() sans paramètre, ce qui a pour effet de charger tous
634 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
635 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
636 génération d'une requête infinie. Affiche la liste de candidats que
637 si le user connecté possède un Evaluateur
638 """
639 qs = self.model._default_manager.get_query_set()
640 return qs.select_related('candidat')
641
642
643 class EvaluateurAdmin(BaseAdmin, VersionAdmin):
644 fieldsets = (
645 ("Utilisateur", {
646 'fields': ('user',)
647 }),
648 )
649
650 ### Actions à afficher
651 def get_actions(self, request):
652 actions = super(EvaluateurAdmin, self).get_actions(request)
653 del actions['delete_selected']
654 return actions
655
656 ### Permissions add, delete, change
657 def has_add_permission(self, request):
658 user_groupes = request.user.groups.all()
659 if request.user.is_superuser is True or \
660 grp_drh in user_groupes or \
661 grp_drh2 in user_groupes or \
662 grp_haute_direction in user_groupes:
663 return True
664 return False
665
666 def has_delete_permission(self, request, obj=None):
667 user_groupes = request.user.groups.all()
668 if request.user.is_superuser is True or \
669 grp_drh in user_groupes or \
670 grp_drh2 in user_groupes or \
671 grp_haute_direction in user_groupes:
672 return True
673 return False
674
675 def has_change_permission(self, request, obj=None):
676 user_groupes = request.user.groups.all()
677 if request.user.is_superuser is True or \
678 grp_drh in user_groupes or \
679 grp_drh2 in user_groupes or \
680 grp_haute_direction in user_groupes:
681 return True
682 return False
683
684
685 class CandidatEvaluationAdmin(BaseAdmin):
686 search_fields = ('candidat__nom', 'candidat__prenom')
687 list_display = (
688 '_candidat', '_statut', '_offre_emploi', 'evaluateur', '_note',
689 '_commentaire'
690 )
691 readonly_fields = ('candidat', 'evaluateur')
692 list_filter = ('candidat__statut', 'candidat__offre_emploi',)
693 fieldsets = (
694 ('Évaluation du candidat', {
695 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
696 }),
697 )
698
699 def get_actions(self, request):
700 # on stocke l'evaluateur connecté (pas forcément la meilleure place...)
701 try:
702 self.evaluateur = Evaluateur.objects.get(user=request.user)
703 except:
704 self.evaluateur = None
705
706 actions = super(CandidatEvaluationAdmin, self).get_actions(request)
707 del actions['delete_selected']
708 return actions
709
710 ### Afficher la note
711 def _note(self, obj):
712 """
713 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
714 un lien pour Évaluer le candidat.
715 Sinon afficher la note.
716 """
717 page = self.model.__name__.lower()
718 redirect_url = 'admin:recrutement_%s_change' % page
719
720 if obj.note is None:
721 label = "Candidat non évalué"
722 else:
723 label = obj.note
724
725 if self.evaluateur == obj.evaluateur:
726 return "<a href='%s'>%s</a>" % (
727 reverse(redirect_url, args=(obj.id,)), label
728 )
729 else:
730 return label
731 _note.allow_tags = True
732 _note.short_description = "Note"
733 _note.admin_order_field = 'note'
734
735 def _statut(self, obj):
736 return obj.candidat.get_statut_display()
737 _statut.order_field = 'candidat__statut'
738 _statut.short_description = 'Statut'
739
740 ### Lien en lecture seule vers le candidat
741 def _candidat(self, obj):
742 return "<a href='%s'>%s</a>" \
743 % (reverse('admin:recrutement_proxycandidat_change',
744 args=(obj.candidat.id,)), obj.candidat)
745 _candidat.allow_tags = True
746 _candidat.short_description = 'Candidat'
747
748 ### Afficher commentaire
749 def _commentaire(self, obj):
750 """
751 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
752 dans le champ commentaire, Aucun au lieu de (None)
753 Sinon afficher la note.
754 """
755 if obj.commentaire is None:
756 return "Aucun"
757 return obj.commentaire
758 _commentaire.allow_tags = True
759 _commentaire.short_description = "Commentaire"
760
761 ### Afficher offre d'emploi
762 def _offre_emploi(self, obj):
763 return "<a href='%s'>%s</a>" % \
764 (reverse('admin:recrutement_proxyoffreemploi_change',
765 args=(obj.candidat.offre_emploi.id,)), obj.candidat.offre_emploi)
766 _offre_emploi.allow_tags = True
767 _offre_emploi.short_description = "Voir offre d'emploi"
768
769 def has_add_permission(self, request):
770 return False
771
772 def has_delete_permission(self, request, obj=None):
773 return False
774
775 def has_change_permission(self, request, obj=None):
776 """
777 Permettre la visualisation dans la changelist
778 mais interdire l'accès à modifier l'objet si l'évaluateur n'est pas
779 le request.user
780 """
781 user_groupes = request.user.groups.all()
782
783 if request.user.is_superuser or \
784 grp_drh in user_groupes or \
785 grp_drh2 in user_groupes or \
786 grp_correspondants_rh in user_groupes or \
787 grp_directeurs_bureau in user_groupes or \
788 grp_administrateurs in user_groupes or \
789 grp_haute_direction in user_groupes:
790 is_recrutement = True
791 else:
792 is_recrutement = False
793
794 return is_recrutement
795
796 def queryset(self, request):
797 """
798 Afficher uniquement les évaluations de l'évaluateur, sauf si
799 l'utilisateur est dans les groupes suivants.
800 """
801 qs = self.model._default_manager.get_query_set() \
802 .select_related('offre_emploi')
803 user_groupes = request.user.groups.all()
804
805 if grp_drh in user_groupes or \
806 grp_drh2 in user_groupes or \
807 grp_correspondants_rh in user_groupes or \
808 grp_directeurs_bureau in user_groupes or \
809 grp_administrateurs in user_groupes or \
810 grp_haute_direction in user_groupes:
811 return qs.filter(candidat__statut__in=('REC', 'SEL'))
812
813 evaluateur = Evaluateur.objects.get(user=request.user)
814 candidats_evaluations = \
815 CandidatEvaluation.objects.filter(evaluateur=evaluateur,
816 candidat__statut__in=('REC', ))
817 candidats_evaluations_ids = [ce.id for ce in candidats_evaluations]
818 return qs.filter(id__in=candidats_evaluations_ids)
819
820
821 class MesCandidatEvaluationAdmin(CandidatEvaluationAdmin):
822 list_filter = []
823
824 def has_change_permission(self, request, obj=None):
825 try:
826 Evaluateur.objects.get(user=request.user)
827 is_evaluateur = True
828 except:
829 is_evaluateur = False
830
831 if obj is None and is_evaluateur:
832 return True
833
834 try:
835 return request.user == obj.evaluateur.user
836 except:
837 return False
838
839 def queryset(self, request):
840 qs = self.model._default_manager.get_query_set() \
841 .select_related('offre_emploi')
842 evaluateur = Evaluateur.objects.get(user=request.user)
843 candidats_evaluations = \
844 CandidatEvaluation.objects.filter(evaluateur=evaluateur,
845 candidat__statut__in=('REC', ))
846 candidats_evaluations_ids = [ce.id for ce in candidats_evaluations]
847 return qs.filter(id__in=candidats_evaluations_ids)
848
849
850 class CourrielTemplateAdmin(BaseAdmin, VersionAdmin):
851 ### Actions à afficher
852 def get_actions(self, request):
853 actions = super(CourrielTemplateAdmin, self).get_actions(request)
854 del actions['delete_selected']
855 return actions
856
857 admin.site.register(OffreEmploi, OffreEmploiAdmin)
858 admin.site.register(ProxyOffreEmploi, ProxyOffreEmploiAdmin)
859 admin.site.register(Candidat, CandidatAdmin)
860 admin.site.register(ProxyCandidat, ProxyCandidatAdmin)
861 admin.site.register(CandidatEvaluation, CandidatEvaluationAdmin)
862 admin.site.register(MesCandidatEvaluation, MesCandidatEvaluationAdmin)
863 admin.site.register(Evaluateur, EvaluateurAdmin)
864 admin.site.register(CourrielTemplate, CourrielTemplateAdmin)