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