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