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