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