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