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