test 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.HAUTE_DIRECTION in user_groupes:
217 return True
218
219 if obj is not None:
220 employe = groups.get_employe_from_user(request.user)
221 if (groups.DIRECTEUR_DE_BUREAU in user_groupes or \
222 groups.ADMINISTRATEURS in user_groupes) and (
223 employe.implantation.region == obj.lieu_affectation.region):
224 return True
225 else:
226 if groups.DIRECTEUR_DE_BUREAU in user_groupes or \
227 groups.ADMINISTRATEURS in user_groupes:
228 return True
229
230
231 return False
232
233
234 class OffreEmploiAdmin(VersionAdmin, OffreEmploiAdminMixin):
235 pass
236
237
238 class ProxyOffreEmploiAdmin(OffreEmploiAdminMixin):
239 list_display = (
240 'nom', 'date_limite', 'region', 'statut', 'est_affiche'
241 )
242 readonly_fields = (
243 'description', 'bureau', 'duree_affectation', 'renumeration',
244 'debut_affectation', 'lieu_affectation', 'nom', 'resume',
245 'date_limite', 'region', 'poste'
246 )
247 fieldsets = (
248 ('Nom', {
249 'fields': ('nom',)
250 }),
251 ('Description générale', {
252 'fields': ('description', 'date_limite',)
253 }),
254 ('Coordonnées', {
255 'fields': ('lieu_affectation', 'bureau', 'region', 'poste',)
256 }),
257 ('Autre', {
258 'fields': (
259 'debut_affectation', 'duree_affectation', 'renumeration',
260 )
261 }),
262 )
263 inlines = []
264
265 ### Lieu de redirection après le change
266 def response_change(self, request, obj):
267 return redirect('admin:recrutement_proxyoffreemploi_changelist')
268
269 ### Formulaire
270 def get_form(self, request, obj=None, **kwargs):
271 form = super(ProxyOffreEmploiAdmin, self).get_form(request, obj, **kwargs)
272 return form
273
274 ### Permissions add, delete, change
275 def has_add_permission(self, request):
276 return False
277
278 def has_delete_permission(self, request, obj=None):
279 return False
280
281 def has_change_permission(self, request, obj=None):
282 user_groupes = [g.name for g in request.user.groups.all()]
283 if request.user.is_superuser is True or \
284 groups.CORRESPONDANT_RH in user_groupes or \
285 groups.DRH_NIVEAU_1 in user_groupes or \
286 groups.DRH_NIVEAU_2 in user_groupes or \
287 groups.DIRECTEUR_DE_BUREAU in user_groupes or \
288 groups.ADMINISTRATEURS in user_groupes or \
289 groups.HAUTE_DIRECTION in user_groupes:
290 return True
291
292 if obj is not None:
293 return True
294
295 return False
296
297
298 class CandidatPieceInline(admin.TabularInline):
299 model = CandidatPiece
300 fields = ('candidat', 'nom', 'path',)
301 extra = 1
302 max_num = 3
303
304
305 class ReadOnlyCandidatPieceInline(CandidatPieceInline):
306 readonly_fields = ('candidat', 'nom', 'path', )
307 cand_delete = False
308
309
310 class CandidatEvaluationInlineFormSet(BaseInlineFormSet):
311 """
312 Empêche la suppression d'une évaluation pour le CandidatEvaluationInline
313 """
314 def __init__(self, *args, **kwargs):
315 super(CandidatEvaluationInlineFormSet, self).__init__(*args, **kwargs)
316 self.can_delete = False
317
318
319 class CandidatEvaluationInline(admin.TabularInline):
320 model = CandidatEvaluation
321 fields = ('evaluateur', 'note', 'commentaire')
322 max_num = 0
323 extra = 0
324 formset = CandidatEvaluationInlineFormSet
325
326 ### Fields readonly
327 def get_readonly_fields(self, request, obj=None):
328 """
329 Empêche la modification des évaluations
330 """
331 if obj:
332 return self.readonly_fields + ('evaluateur', 'note', 'commentaire')
333 return self.readonly_fields
334
335
336 class CandidatAdminMixin(BaseAdmin):
337 search_fields = ('nom', 'prenom')
338 exclude = ('actif', )
339 list_editable = ('statut', )
340 list_display = ('_candidat', 'offre_emploi',
341 'voir_offre_emploi', 'calculer_moyenne',
342 'afficher_candidat', '_date_creation', 'statut', )
343 list_filter = ('offre_emploi', 'offre_emploi__region', 'statut', )
344
345 fieldsets = (
346 ("Offre d'emploi", {
347 'fields': ('offre_emploi', )
348 }),
349 ('Informations personnelles', {
350 'fields': (
351 'prenom', 'nom', 'genre', 'nationalite',
352 'situation_famille', 'nombre_dependant'
353 )
354 }),
355 ('Coordonnées', {
356 'fields': (
357 'telephone', 'email', 'adresse', 'ville', 'etat_province',
358 'code_postal', 'pays'
359 )
360 }),
361 ('Informations professionnelles', {
362 'fields': (
363 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
364 'domaine_professionnel'
365 )
366 }),
367 ('Traitement', {
368 'fields': ('statut', )
369 }),
370 )
371 inlines = [
372 CandidatPieceInline,
373 CandidatEvaluationInline,
374 ]
375 actions = ['envoyer_courriel_candidats']
376
377 def _candidat(self, obj):
378 txt = u"%s %s (%s)" % (obj.nom.upper(), obj.prenom, obj.genre)
379 txt = textwrap.wrap(txt, 30)
380 return "<br/>".join(txt)
381 _candidat.short_description = "Candidat"
382 _candidat.admin_order_field = "nom"
383 _candidat.allow_tags = True
384
385 def _date_creation(self, obj):
386 return obj.date_creation
387 _date_creation.admin_order_field = "date_creation"
388 _date_creation.short_description = "Date de réception"
389
390 ### Actions à afficher
391 def get_actions(self, request):
392 actions = super(CandidatAdmin, self).get_actions(request)
393 del actions['delete_selected']
394 return actions
395
396 ### Envoyer un courriel à des candidats
397 def envoyer_courriel_candidats(modeladmin, obj, candidats):
398 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
399
400 return HttpResponseRedirect(
401 reverse('selectionner_template') + "?ids=%s" % (",".join(selected))
402 )
403 envoyer_courriel_candidats.short_description = u'Envoyer courriel'
404
405 ### Évaluer un candidat
406 def evaluer_candidat(self, obj):
407 return "<a href='%s?candidat__id__exact=%s'>" \
408 "Évaluer le candidat</a>" % (
409 reverse('admin:recrutement_candidatevaluation_changelist'),
410 obj.id
411 )
412 evaluer_candidat.allow_tags = True
413 evaluer_candidat.short_description = 'Évaluation'
414
415 ### Afficher un candidat
416 def afficher_candidat(self, obj):
417 items = [u"<li><a href='%s%s'>%s</li>" % \
418 (settings.OE_PRIVE_MEDIA_URL, pj.path, pj.get_nom_display()) \
419 for pj in obj.pieces_jointes()]
420 html = "<a href='%s'>Voir le candidat</a>" % (
421 reverse('admin:recrutement_proxycandidat_change', args=(obj.id,))
422 )
423 return "%s<ul>%s</ul>" % (html, "\n".join(items))
424 afficher_candidat.allow_tags = True
425 afficher_candidat.short_description = u'Détails du candidat'
426
427 ### Voir l'offre d'emploi
428 def voir_offre_emploi(self, obj):
429 return "<a href='%s'>Voir l'offre d'emploi</a>" % (reverse(
430 'admin:recrutement_proxyoffreemploi_change',
431 args=(obj.offre_emploi.id,)
432 ))
433 voir_offre_emploi.allow_tags = True
434 voir_offre_emploi.short_description = "Afficher l'offre d'emploi"
435
436 ### Calculer la moyenne des notes
437 def calculer_moyenne(self, obj):
438 evaluations = CandidatEvaluation.objects.filter(candidat=obj)
439
440 notes = [evaluation.note for evaluation in evaluations \
441 if evaluation.note is not None]
442
443 if len(notes) > 0:
444 moyenne_votes = round(float(sum(notes)) / len(notes), 2)
445 else:
446 moyenne_votes = "Non disponible"
447
448 totales = len(evaluations)
449 faites = len(notes)
450
451 if obj.statut == 'REC':
452 if totales == faites:
453 color = "green"
454 elif faites > 0 and float(totales) / float(faites) >= 2:
455 color = "orange"
456 else:
457 color = "red"
458 else:
459 color = "black"
460
461 return """<span style="color: %s;">%s (%s/%s)</span>""" % (
462 color, moyenne_votes, faites, totales
463 )
464 calculer_moyenne.allow_tags = True
465 calculer_moyenne.short_description = "Moyenne"
466 calculer_moyenne.admin_order_field = ""
467
468 ### Permissions add, delete, change
469 def has_add_permission(self, request):
470 user_groupes = [g.name for g in request.user.groups.all()]
471 if request.user.is_superuser is True or \
472 groups.CORRESPONDANT_RH in user_groupes or \
473 groups.DRH_NIVEAU_1 in user_groupes or \
474 groups.DRH_NIVEAU_2 in user_groupes or \
475 groups.DIRECTEUR_DE_BUREAU in user_groupes or \
476 groups.ADMINISTRATEURS in user_groupes or \
477 groups.HAUTE_DIRECTION in user_groupes:
478 return True
479 return False
480
481 def has_delete_permission(self, request, obj=None):
482 user_groupes = [g.name for g in request.user.groups.all()]
483 if request.user.is_superuser is True or \
484 groups.CORRESPONDANT_RH in user_groupes or \
485 groups.DRH_NIVEAU_1 in user_groupes or \
486 groups.DRH_NIVEAU_2 in user_groupes or \
487 groups.DIRECTEUR_DE_BUREAU in user_groupes or \
488 groups.ADMINISTRATEURS in user_groupes or \
489 groups.HAUTE_DIRECTION in user_groupes:
490 return True
491 return False
492
493 def has_change_permission(self, request, obj=None):
494 user_groupes = [g.name for g in request.user.groups.all()]
495 if request.user.is_superuser is True or \
496 groups.CORRESPONDANT_RH in user_groupes or \
497 groups.DRH_NIVEAU_1 in user_groupes or \
498 groups.DRH_NIVEAU_2 in user_groupes or \
499 groups.DIRECTEUR_DE_BUREAU in user_groupes or \
500 groups.ADMINISTRATEURS in user_groupes or \
501 groups.HAUTE_DIRECTION in user_groupes:
502 return True
503 return False
504
505 def get_changelist(self, request, **kwargs):
506 return OrderedChangeList
507
508 def queryset(self, request):
509 """
510 Spécifie un queryset limité, autrement Django exécute un
511 select_related() sans paramètre, ce qui a pour effet de charger tous
512 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
513 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
514 génération d'une requête infinie.
515 """
516 qs = self.model._default_manager.get_query_set() \
517 .select_related('offre_emploi') \
518 .annotate(moyenne=Avg('evaluations__note'))
519
520 user_groupes = [g.name for g in request.user.groups.all()]
521 if groups.DRH_NIVEAU_1 in user_groupes or \
522 groups.DRH_NIVEAU_2 in user_groupes or \
523 groups.HAUTE_DIRECTION in user_groupes:
524 return qs
525
526 if groups.DIRECTEUR_DE_BUREAU in user_groupes or \
527 groups.CORRESPONDANT_RH in user_groupes or \
528 groups.ADMINISTRATEURS in user_groupes:
529 employe = groups.get_employe_from_user(request.user)
530 return qs.filter(offre_emploi__region=employe.implantation.region)
531
532 if Evaluateur.objects.filter(user=request.user).exists():
533 evaluateur = Evaluateur.objects.get(user=request.user)
534 candidat_ids = [e.candidat.id for e in
535 CandidatEvaluation.objects.filter(evaluateur=evaluateur)]
536 return qs.filter(id__in=candidat_ids)
537 return qs.none()
538
539
540 class CandidatAdmin(VersionAdmin, CandidatAdminMixin):
541 pass
542
543
544 class ProxyCandidatAdmin(CandidatAdminMixin):
545 list_editable = ()
546 readonly_fields = (
547 'statut', 'offre_emploi', 'prenom', 'nom', 'genre', 'nationalite',
548 'situation_famille', 'nombre_dependant', 'telephone', 'email',
549 'adresse', 'ville', 'etat_province', 'code_postal', 'pays',
550 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
551 'domaine_professionnel', 'pieces_jointes'
552 )
553 fieldsets = (
554 ("Offre d'emploi", {
555 'fields': ('offre_emploi', )
556 }),
557 ('Informations personnelles', {
558 'fields': (
559 'prenom', 'nom', 'genre', 'nationalite', 'situation_famille',
560 'nombre_dependant'
561 )
562 }),
563 ('Coordonnées', {
564 'fields': (
565 'telephone', 'email', 'adresse', 'ville', 'etat_province',
566 'code_postal', 'pays'
567 )
568 }),
569 ('Informations professionnelles', {
570 'fields': (
571 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
572 'domaine_professionnel'
573 )
574 }),
575 )
576 inlines = (CandidatEvaluationInline, )
577
578 def has_add_permission(self, request):
579 return False
580
581 def has_delete_permission(self, request, obj=None):
582 return False
583
584 def has_change_permission(self, request, obj=None):
585 user_groupes = [g.name for g in request.user.groups.all()]
586 if request.user.is_superuser is True or \
587 groups.CORRESPONDANT_RH in user_groupes or \
588 groups.DRH_NIVEAU_1 in user_groupes or \
589 groups.DRH_NIVEAU_2 in user_groupes or \
590 groups.DIRECTEUR_DE_BUREAU in user_groupes or \
591 groups.ADMINISTRATEURS in user_groupes or \
592 groups.HAUTE_DIRECTION in user_groupes:
593 return True
594
595 if obj is not None:
596 evaluateur = Evaluateur.objects.get(user=request.user)
597 for e in obj.evaluations.all():
598 if e.evaluateur == evaluateur:
599 return True
600
601 return False
602
603 def get_actions(self, request):
604 return None
605
606
607 class CandidatPieceAdmin(admin.ModelAdmin):
608 list_display = ('nom', 'candidat', )
609
610 ### Queryset
611 def queryset(self, request):
612 """
613 Spécifie un queryset limité, autrement Django exécute un
614 select_related() sans paramètre, ce qui a pour effet de charger tous
615 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
616 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
617 génération d'une requête infinie. Affiche la liste de candidats que
618 si le user connecté possède un Evaluateur
619 """
620 qs = self.model._default_manager.get_query_set()
621 return qs.select_related('candidat')
622
623
624 class EvaluateurAdmin(BaseAdmin, VersionAdmin):
625 fieldsets = (
626 ("Utilisateur", {
627 'fields': ('user',)
628 }),
629 )
630
631 ### Actions à afficher
632 def get_actions(self, request):
633 actions = super(EvaluateurAdmin, self).get_actions(request)
634 del actions['delete_selected']
635 return actions
636
637 ### Permissions add, delete, change
638 def has_add_permission(self, request):
639 user_groupes = [g.name for g in request.user.groups.all()]
640 if request.user.is_superuser is True or \
641 groups.DRH_NIVEAU_1 in user_groupes or \
642 groups.DRH_NIVEAU_2 in user_groupes or \
643 groups.HAUTE_DIRECTION in user_groupes:
644 return True
645 return False
646
647 def has_delete_permission(self, request, obj=None):
648 user_groupes = [g.name for g in request.user.groups.all()]
649 if request.user.is_superuser is True or \
650 groups.DRH_NIVEAU_1 in user_groupes or \
651 groups.DRH_NIVEAU_2 in user_groupes or \
652 groups.HAUTE_DIRECTION in user_groupes:
653 return True
654 return False
655
656 def has_change_permission(self, request, obj=None):
657 user_groupes = [g.name for g in request.user.groups.all()]
658 if request.user.is_superuser is True or \
659 groups.DRH_NIVEAU_1 in user_groupes or \
660 groups.DRH_NIVEAU_2 in user_groupes or \
661 groups.HAUTE_DIRECTION in user_groupes:
662 return True
663 return False
664
665
666 class CandidatEvaluationAdmin(BaseAdmin):
667 search_fields = ('candidat__nom', 'candidat__prenom')
668 list_display = (
669 '_candidat', '_statut', '_offre_emploi', 'evaluateur', '_note',
670 '_commentaire'
671 )
672 readonly_fields = ('candidat', 'evaluateur')
673 list_filter = ('candidat__statut', 'candidat__offre_emploi',)
674 fieldsets = (
675 ('Évaluation du candidat', {
676 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
677 }),
678 )
679
680 def get_actions(self, request):
681 # on stocke l'evaluateur connecté (pas forcément la meilleure place...)
682 try:
683 self.evaluateur = Evaluateur.objects.get(user=request.user)
684 except:
685 self.evaluateur = None
686
687 actions = super(CandidatEvaluationAdmin, self).get_actions(request)
688 del actions['delete_selected']
689 return actions
690
691 ### Afficher la note
692 def _note(self, obj):
693 """
694 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
695 un lien pour Évaluer le candidat.
696 Sinon afficher la note.
697 """
698 page = self.model.__name__.lower()
699 redirect_url = 'admin:recrutement_%s_change' % page
700
701 if obj.note is None:
702 label = "Candidat non évalué"
703 else:
704 label = obj.note
705
706 if self.evaluateur == obj.evaluateur:
707 return "<a href='%s'>%s</a>" % (
708 reverse(redirect_url, args=(obj.id,)), label
709 )
710 else:
711 return label
712 _note.allow_tags = True
713 _note.short_description = "Note"
714 _note.admin_order_field = 'note'
715
716 def _statut(self, obj):
717 return obj.candidat.get_statut_display()
718 _statut.order_field = 'candidat__statut'
719 _statut.short_description = 'Statut'
720
721 ### Lien en lecture seule vers le candidat
722 def _candidat(self, obj):
723 return "<a href='%s'>%s</a>" \
724 % (reverse('admin:recrutement_proxycandidat_change',
725 args=(obj.candidat.id,)), obj.candidat)
726 _candidat.allow_tags = True
727 _candidat.short_description = 'Candidat'
728
729 ### Afficher commentaire
730 def _commentaire(self, obj):
731 """
732 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
733 dans le champ commentaire, Aucun au lieu de (None)
734 Sinon afficher la note.
735 """
736 if obj.commentaire is None:
737 return "Aucun"
738 return obj.commentaire
739 _commentaire.allow_tags = True
740 _commentaire.short_description = "Commentaire"
741
742 ### Afficher offre d'emploi
743 def _offre_emploi(self, obj):
744 return "<a href='%s'>%s</a>" % \
745 (reverse('admin:recrutement_proxyoffreemploi_change',
746 args=(obj.candidat.offre_emploi.id,)), obj.candidat.offre_emploi)
747 _offre_emploi.allow_tags = True
748 _offre_emploi.short_description = "Voir offre d'emploi"
749
750 def has_add_permission(self, request):
751 return False
752
753 def has_delete_permission(self, request, obj=None):
754 return False
755
756 def has_change_permission(self, request, obj=None):
757 """
758 Permettre la visualisation dans la changelist
759 mais interdire l'accès à modifier l'objet si l'évaluateur n'est pas
760 le request.user
761 """
762 user_groupes = [g.name for g in request.user.groups.all()]
763
764 if request.user.is_superuser or \
765 groups.CORRESPONDANT_RH in user_groupes or \
766 groups.DRH_NIVEAU_1 in user_groupes or \
767 groups.DRH_NIVEAU_2 in user_groupes or \
768 groups.DIRECTEUR_DE_BUREAU in user_groupes or \
769 groups.ADMINISTRATEURS in user_groupes or \
770 groups.HAUTE_DIRECTION in user_groupes:
771 is_recrutement = True
772 else:
773 is_recrutement = False
774
775 return is_recrutement
776
777 def queryset(self, request):
778 """
779 Afficher uniquement les évaluations de l'évaluateur, sauf si
780 l'utilisateur est dans les groupes suivants.
781 """
782 qs = self.model._default_manager.get_query_set() \
783 .select_related('offre_emploi')
784 user_groupes = request.user.groups.all()
785 user_groupes = [g.name for g in request.user.groups.all()]
786
787 if request.user.is_superuser or \
788 groups.CORRESPONDANT_RH in user_groupes or \
789 groups.DRH_NIVEAU_1 in user_groupes or \
790 groups.DRH_NIVEAU_2 in user_groupes or \
791 groups.DIRECTEUR_DE_BUREAU in user_groupes or \
792 groups.ADMINISTRATEURS in user_groupes or \
793 groups.HAUTE_DIRECTION in user_groupes:
794 return qs
795
796 evaluateur = Evaluateur.objects.get(user=request.user)
797 candidats_evaluations = \
798 CandidatEvaluation.objects.filter(evaluateur=evaluateur,
799 candidat__statut__in=('REC', ))
800 candidats_evaluations_ids = [ce.id for ce in candidats_evaluations]
801 return qs.filter(id__in=candidats_evaluations_ids)
802
803
804 class MesCandidatEvaluationAdmin(CandidatEvaluationAdmin):
805
806 def has_change_permission(self, request, obj=None):
807 try:
808 Evaluateur.objects.get(user=request.user)
809 is_evaluateur = True
810 except:
811 is_evaluateur = False
812
813 if obj is None and is_evaluateur:
814 return True
815
816 try:
817 return request.user == obj.evaluateur.user
818 except:
819 return False
820
821 def queryset(self, request):
822 qs = self.model._default_manager.get_query_set() \
823 .select_related('offre_emploi')
824 evaluateur = Evaluateur.objects.get(user=request.user)
825 candidats_evaluations = \
826 CandidatEvaluation.objects.filter(evaluateur=evaluateur,
827 candidat__statut__in=('REC', ))
828 candidats_evaluations_ids = [ce.id for ce in candidats_evaluations]
829 return qs.filter(id__in=candidats_evaluations_ids)
830
831
832 class CourrielTemplateAdmin(BaseAdmin, VersionAdmin):
833 ### Actions à afficher
834 def get_actions(self, request):
835 actions = super(CourrielTemplateAdmin, self).get_actions(request)
836 del actions['delete_selected']
837 return actions
838
839 admin.site.register(OffreEmploi, OffreEmploiAdmin)
840 admin.site.register(ProxyOffreEmploi, ProxyOffreEmploiAdmin)
841 admin.site.register(Candidat, CandidatAdmin)
842 admin.site.register(ProxyCandidat, ProxyCandidatAdmin)
843 admin.site.register(CandidatEvaluation, CandidatEvaluationAdmin)
844 admin.site.register(MesCandidatEvaluation, MesCandidatEvaluationAdmin)
845 admin.site.register(Evaluateur, EvaluateurAdmin)
846 admin.site.register(CourrielTemplate, CourrielTemplateAdmin)