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